Ecommerce store template built with Next.js App Router, Medusa and TailwindCSS, with features like Algolia search and Stripe checkout.
To use the Next.js Starter Template, you should have a Medusa server running locally on port 9000. For a quick setup, run:
npx create-medusa-app@latest
Check out create-medusa-app docs for more details and troubleshooting.
The Medusa Next.js Starter is built with:
Features include:
Navigate into your projects directory and get your environment variables ready:
cd nextjs-starter-medusa/mv .env.template .env.local
Use Yarn to install all dependencies.
yarn
You are now ready to start up your project.
yarn dev
Your site is now running at http://localhost:8000!
By default this starter supports the following payment integrations
To enable the integrations you need to add the following to your .env.local
file:
NEXT_PUBLIC_STRIPE_KEY=<your-stripe-public-key>NEXT_PUBLIC_PAYPAL_CLIENT_ID=<your-paypal-client-id>
You will also need to setup the integrations in your Medusa server. See the Medusa documentation for more information on how to configure Stripe and PayPal in your Medusa project.
This starter is configured to support using the medusa-search-meilisearch
plugin out of the box. To enable search you will need to enable the feature flag in ./store.config.json
, which you do by changing the config to this:
{"features": {// other features..."search": true}}
Before you can search you will need to install the plugin in your Medusa server, for a written guide on how to do this – see our documentation.
The search components in this starter are developed with Algolia's react-instant-search-hooks-web
library which should make it possible for you to seemlesly change your search provider to Algolia instead of MeiliSearch.
To do this you will need to add algoliasearch
to the project, by running
yarn add algoliasearch
After this you will need to switch the current MeiliSearch SearchClient
out with a Alogolia client. To do this update @lib/search-client
.
import algoliasearch from "algoliasearch/lite"const appId = process.env.NEXT_PUBLIC_SEARCH_APP_ID || "test_app_id" // You should add this to your environment variablesconst apiKey = process.env.NEXT_PUBLIC_SEARCH_API_KEY || "test_key"export const searchClient = algoliasearch(appId, apiKey)export const SEARCH_INDEX_NAME =process.env.NEXT_PUBLIC_INDEX_NAME || "products"
Then, in src/app/(main)/search/actions.ts
, remove the MeiliSearch code (line 10-16) and uncomment the Algolia code.
"use server"import { searchClient, SEARCH_INDEX_NAME } from "@lib/search-client"/*** Uses MeiliSearch or Algolia to search for a query* @param {string} query - search query*/export async function search(query: string) {const index = searchClient.initIndex(SEARCH_INDEX_NAME)const { hits } = await index.search(query)return hits}
After this you will need to set up Algolia with your Medusa server, and then you should be good to go. For a more thorough walkthrough of using Algolia with Medusa – see our documentation, and the documentation for using react-instantsearch-hooks-web
.
For the new version, the main folder structure remains unchanged. The contents have changed quite a bit though.
.└── src├── app├── lib├── modules├── styles├── types└── middleware.ts
/app
directoryThe app folder contains all Next.js App Router pages and layouts, and takes care of the routing.
.└── [countryCode]├── (checkout)└── checkout└── (main)├── account│ ├── addresses│ └── orders│ └── details│ └── [id]├── cart├── categories│ └── [...category]├── collections│ └── [handle]├── order│ └── confirmed│ └── [id]├── products│ └── [handle]├── results│ └── [query]├── search└── store
The app router folder structure represents the routes of the Starter. In this case, the structure is as follows:
[countryCode]
folder. This indicates a dynamic route based on the country code. The this will be populated by the countries you set up in your Medusa server. The param is then used to fetch region specific prices, languages, etc.(checkout)
and (main)
. This is done because the checkout flow uses a different layout. All other parts of the app share the same layout and are in subdirectories of the (main)
group. Route Groups do not affect the url.account
directory has addresses
and orders
subdirectories. The orders
directory further has a details
subdirectory, which itself has a dynamic [id]
subdirectory./account/orders/details/123
would correspond to the account > orders > details > [id]
path in the router structure, with 123
being the dynamic [id]
.This structure enables efficient routing and organization of different parts of the Starter.
/lib
directoryThe lib directory contains all utilities like the Medusa JS client functions, util functions, config and constants.
The most important file here is /lib/data/index.ts
. This file defines various functions for interacting with the Medusa API, using the JS client. The functions cover a range of actions related to shopping carts, orders, shipping, authentication, customer management, regions, products, collections, and categories. It also includes utility functions for handling headers and errors, as well as some functions for sorting and transforming product data.
These functions are used in different Server Actions.
/modules
directoryThis is where all the components, templates and Server Actions are, grouped by section. Some subdirectories have an actions.ts
file. These files contain all Server Actions relevant to that section of the app.
/styles
directoryglobal.css
imports Tailwind classes and defines a couple of global CSS classes. Tailwind and Medusa UI classes are used for styling throughout the app.
/types
directoryContains global TypeScript type defintions.
middleware.ts
Next.js Middleware, which is basically an Edge function that runs before (almost) every request. In our case it enforces a countryCode
in the url. So when a user visits any url on your storefront without a countryCode
param, it will redirect the user to the url for the most relevant region.
The region will be decided as follows:
x-vercel-ip-country
header.NEXT_PUBLIC_DEFAULT_REGION
environment variable, it will redirect to that.If you want to use the countryCode
param in your code, there’s two ways to do that:
On the server in any page.tsx
- the countryCode
is in the params
object:
export default async function Page({params: { countryCode },}: {params: { countryCode: string }}) {const region = await getRegion(countryCode)// rest of code
From client components, with the useParam
hook:
import { useParams } from "next/navigation"const Component = () => {const { countryCode } = useParams()// rest of code
The middleware also sets a cookie based on the onboarding status of a user. This is related to the Medusa Admin onboarding flow, and may be safely removed in your production storefront.
Ecommerce store template built with Next.js App Router, Medusa and TailwindCSS, with features like Algolia search and Stripe checkout.
To use the Next.js Starter Template, you should have a Medusa server running locally on port 9000. For a quick setup, run:
npx create-medusa-app@latest
Check out create-medusa-app docs for more details and troubleshooting.
The Medusa Next.js Starter is built with:
Features include:
Navigate into your projects directory and get your environment variables ready:
cd nextjs-starter-medusa/mv .env.template .env.local
Use Yarn to install all dependencies.
yarn
You are now ready to start up your project.
yarn dev
Your site is now running at http://localhost:8000!
By default this starter supports the following payment integrations
To enable the integrations you need to add the following to your .env.local
file:
NEXT_PUBLIC_STRIPE_KEY=<your-stripe-public-key>NEXT_PUBLIC_PAYPAL_CLIENT_ID=<your-paypal-client-id>
You will also need to setup the integrations in your Medusa server. See the Medusa documentation for more information on how to configure Stripe and PayPal in your Medusa project.
This starter is configured to support using the medusa-search-meilisearch
plugin out of the box. To enable search you will need to enable the feature flag in ./store.config.json
, which you do by changing the config to this:
{"features": {// other features..."search": true}}
Before you can search you will need to install the plugin in your Medusa server, for a written guide on how to do this – see our documentation.
The search components in this starter are developed with Algolia's react-instant-search-hooks-web
library which should make it possible for you to seemlesly change your search provider to Algolia instead of MeiliSearch.
To do this you will need to add algoliasearch
to the project, by running
yarn add algoliasearch
After this you will need to switch the current MeiliSearch SearchClient
out with a Alogolia client. To do this update @lib/search-client
.
import algoliasearch from "algoliasearch/lite"const appId = process.env.NEXT_PUBLIC_SEARCH_APP_ID || "test_app_id" // You should add this to your environment variablesconst apiKey = process.env.NEXT_PUBLIC_SEARCH_API_KEY || "test_key"export const searchClient = algoliasearch(appId, apiKey)export const SEARCH_INDEX_NAME =process.env.NEXT_PUBLIC_INDEX_NAME || "products"
Then, in src/app/(main)/search/actions.ts
, remove the MeiliSearch code (line 10-16) and uncomment the Algolia code.
"use server"import { searchClient, SEARCH_INDEX_NAME } from "@lib/search-client"/*** Uses MeiliSearch or Algolia to search for a query* @param {string} query - search query*/export async function search(query: string) {const index = searchClient.initIndex(SEARCH_INDEX_NAME)const { hits } = await index.search(query)return hits}
After this you will need to set up Algolia with your Medusa server, and then you should be good to go. For a more thorough walkthrough of using Algolia with Medusa – see our documentation, and the documentation for using react-instantsearch-hooks-web
.
For the new version, the main folder structure remains unchanged. The contents have changed quite a bit though.
.└── src├── app├── lib├── modules├── styles├── types└── middleware.ts
/app
directoryThe app folder contains all Next.js App Router pages and layouts, and takes care of the routing.
.└── [countryCode]├── (checkout)└── checkout└── (main)├── account│ ├── addresses│ └── orders│ └── details│ └── [id]├── cart├── categories│ └── [...category]├── collections│ └── [handle]├── order│ └── confirmed│ └── [id]├── products│ └── [handle]├── results│ └── [query]├── search└── store
The app router folder structure represents the routes of the Starter. In this case, the structure is as follows:
[countryCode]
folder. This indicates a dynamic route based on the country code. The this will be populated by the countries you set up in your Medusa server. The param is then used to fetch region specific prices, languages, etc.(checkout)
and (main)
. This is done because the checkout flow uses a different layout. All other parts of the app share the same layout and are in subdirectories of the (main)
group. Route Groups do not affect the url.account
directory has addresses
and orders
subdirectories. The orders
directory further has a details
subdirectory, which itself has a dynamic [id]
subdirectory./account/orders/details/123
would correspond to the account > orders > details > [id]
path in the router structure, with 123
being the dynamic [id]
.This structure enables efficient routing and organization of different parts of the Starter.
/lib
directoryThe lib directory contains all utilities like the Medusa JS client functions, util functions, config and constants.
The most important file here is /lib/data/index.ts
. This file defines various functions for interacting with the Medusa API, using the JS client. The functions cover a range of actions related to shopping carts, orders, shipping, authentication, customer management, regions, products, collections, and categories. It also includes utility functions for handling headers and errors, as well as some functions for sorting and transforming product data.
These functions are used in different Server Actions.
/modules
directoryThis is where all the components, templates and Server Actions are, grouped by section. Some subdirectories have an actions.ts
file. These files contain all Server Actions relevant to that section of the app.
/styles
directoryglobal.css
imports Tailwind classes and defines a couple of global CSS classes. Tailwind and Medusa UI classes are used for styling throughout the app.
/types
directoryContains global TypeScript type defintions.
middleware.ts
Next.js Middleware, which is basically an Edge function that runs before (almost) every request. In our case it enforces a countryCode
in the url. So when a user visits any url on your storefront without a countryCode
param, it will redirect the user to the url for the most relevant region.
The region will be decided as follows:
x-vercel-ip-country
header.NEXT_PUBLIC_DEFAULT_REGION
environment variable, it will redirect to that.If you want to use the countryCode
param in your code, there’s two ways to do that:
On the server in any page.tsx
- the countryCode
is in the params
object:
export default async function Page({params: { countryCode },}: {params: { countryCode: string }}) {const region = await getRegion(countryCode)// rest of code
From client components, with the useParam
hook:
import { useParams } from "next/navigation"const Component = () => {const { countryCode } = useParams()// rest of code
The middleware also sets a cookie based on the onboarding status of a user. This is related to the Medusa Admin onboarding flow, and may be safely removed in your production storefront.