Introduction
Building modern ecommerce storefronts can be a challenge. There is a need for both technical prowess and ease of content management of the site. Developers want to build performant and secure sites, while marketers want to push sales through visually appealing storefronts. Usually, tradeoffs have to be made on either side of the equation. This is where Stackbit comes in.
In this step-by-step tutorial, you will learn how to use Stackbit and Medusa to build a custom storefront for an ecommerce website. You will learn how to use the visual building experience that Stackbit’s site builder provides to edit and build the site. Then see how the site integrates with Medusa’s REST API.
What is Stackbit?
Stackbit is a visual experience platform that helps developers build a custom site-building experience for content managers of the site. It provides a mechanism for connecting content sources like a CMS with the frontend of a site all through a visual builder.
Here are some of the benefits of using Stackbit:
- Flexible development and editorial workflow: Developers can bring their own tech stack and use their preferred component libraries and UI kits while content editors can make visual changes using a no-code approach with minimal annotations.
- Real-time updates: Changes made in Stackbit or in the content source are updated instantly, providing a seamless and efficient workflow for both developers and content editors.
- Cloud-based visual editor: Stackbit provides a cloud-based visual editor that picks up code changes automatically and launches an isolated container to render the site's preview.
- Own your content: Stackbit does not store your content and reads and writes content directly from your API CMS or files in Git, allowing you to fully own and control your content.
- Stackbit is fully composable: Stackbit works with any Node-based stack, including popular tools like Next.js, React & Tailwind, allowing for seamless integration with existing workflows and tools.
If you want to build an ecommerce storefront using Stackbit you will need a composable commerce engine to power up the tech stack. This is where Medusa comes in.
What is Medusa?
Medusa is an open source composable commerce backend full of features to help build your ecommerce website. It has a headless server that exposes the REST APIs for your ecommerce store, an admin panel to manage your ecommerce site, and a storefront. It is fully customizable and extensible. You can build webshops, subscription services, digital products stores, ticketing systems, and any other ecommerce solutions.
Here are some of the benefits of Medusa:
- Medusa is fully open source. No paid features or proprietary backends. No limit on the customizations, features, and integrations you can put.
- Medusa has a wide range of ecommerce features including payment providers, cart, checkout, gift codes, multi-currency support, PIM, and many more.
- Medusa is composable. You can set up your store to the specifications that best suit your business needs with seamless third-party integrations.
- Medusa is headless. You can use any frontend framework to interact with it.
- Medusa offers community support despite being free and open source. Through their Discord channel and GitHub discussions, you will have enough help to get past any bottleneck.
Resources
Here is a demo of the website you will build.
Prerequisites
- Node.js (LTS) - *********v14 or later*********
- Git
- Stackbit account
Medusa Server Setup
Medusa provides the ecommerce backend for the storefront you will create. Let’s get the server up and running.
Install Medusa CLI tool
Install the Medusa CLI tool globally on your work machine.
npm install @medusajs/medusa-cli -g
Install Medusa server
Install the Medusa server in your working directory. Name it medusa-store
and seed the server with data.
medusa new medusa-store --seed
Configure CORS Settings
In your text editor open up medusa-store/medusa-config.js
file and update it to allow the Stackbit client to consume the Medusa Store API.
...
// CORS to avoid issues when consuming Medusa from a client
const STORE_CORS = process.env.STORE_CORS || "http://localhost:8000,http://localhost:3000,http://localhost:8090";
...
Start Medusa server
cd medusa-store
medusa develop
Your Medusa server is now running on port 9000.
Test Medusa server
curl localhost:9000/store/products
***OPTIONAL:
You can also visit localhost:9000/store/products in your browser to test your Medusa server.
The response from the Medusa server should be a large JSON payload. Here are the first few lines of the response:
{
"products": [
{
"id": "prod_01GQTERP2S14W3MJFYAMG7AB7S4",
"created_at": "2023-02-04T08:35:19.000Z",
"updated_at": "2023-02-04T08:35:19.000Z",
"deleted_at": null,
"title": "Medusa Coffee Mug",
"subtitle": null,
"description": "Every programmer's best friend.",
"handle": "coffee-mug",
...
The Medusa Quickstart guide has more information related to setting up your server.
Next.js Starter Setup
The ecommerce backend is up and running. Next, you will set up the frontend for your ecommerce store. For the frontend, we will use Stackbit’s Next.js Starter and turn it into a storefront.
It is a nice minimal starting point for you to modify into a storefront. It is already configured to work with Stackbit.
Install Starter
In a new working directory separate from medusa-store
, create a project for the starter.
npx create-stackbit-app@latest --starter nextjs
This creates a new repo in a my-stackbit-site
folder and installs the dependencies for the starter.
Folder Structure
Take a look at the directory structure of the Next.js starter project folder.
cd my-stackbit-site
tree -L 1 -a
Here’s a top level view.
.
├── components
├── content
├── .eslintrc.json
├── .git
├── .gitignore
├── LICENSE
├── netlify.toml
├── .next
├── next.config.js
├── node_modules
├── .nvmrc
├── package.json
├── package-lock.json
├── pages
├── .prettierrc
├── public
├── README.md
├── .stackbit
├── stackbit.config.js
├── styles
└── utils
- The
components
folder contains the components that you use to build the pages. - The
content
folder contains the content (markdown files) for the starter. - The
pages
folder has the[[...slug]].js
file which is a catch-all page template that maps content to the specific component as defined in thecomponents
directory. - The
.stackbit
folder has themodels
folder which contains models which provide the schema definition for Stackbit to understand your content - The
stackbit.config.js
file enables you to customize the visual editing experience of your site. It is already set with the minimal configuration for the Next.js site. - The
utils
folder has acontent.js
file which contains a set of utility functions for reading and parsing the content files in thecontent
folder.
Launch the site
Run the Next.js development server in your starter project folder.
npm run dev
This starts up your Next.js development server on port 3000.
Visit localhost:3000
in your browser to see the home page.
Stackbit Setup
Stackbit uses a content-driven architecture where content and code are separate from each other. To set up a local development server for Stackbit you need to install the Stackbit CLI. You will then use the stackbit dev
command to run the development environment.
The Stackbit development server runs in parallel with the Next.js development server to create the visual editing environment.
Install Stackbit CLI
npm install -g @stackbit/cli
Run Stackbit Dev
Ensure your Next.js development server is still running on port 3000
. Navigate to my-stackbit-site
and start the Stackbit development server.
cd my-stackbit-site
stackbit dev
Both servers should be running in parallel. The Stackbit server will be running on port 8090 start forwarding requests to http://localhost:3000
. Open http://localhost:8090/_stackbit
in your web browser and register or sign in to your Stackbit account.
After signing in to your account, you should be met with Stackbit’s visual editor containing your Next.js project.
Editing Content using Stackbit
Add Header
We are going to start editing content using Stackbit by doing a preliminary setup first. You will first add a model for the Header.
In a new terminal session in the my-stackbit-site
directory, create a new file named .stackbit/models/HeaderConfig.js
cd my-stackbit-site
touch .stackbit/models/HeaderConfig.js
In your text editor, add the following code to your header model.
module.exports = {
label: 'Header Config',
labelField: 'title',
fields: [
{ type: 'string', name: 'title', label: 'Title', default: 'Your Brand' },
{
type: 'list',
name: 'navLinks',
label: 'Navigation Links',
items: {
type: 'object',
fields: [
{ type: 'string', name: 'label', label: 'Label' },
{ type: 'string', name: 'url', label: 'URL' },
],
},
},
],
};
This tells Stackbit that the Header component will have two fields: title
for the website’s title to be displayed on the Header and navLink
for the links to the pages in the website.
Add a component for the Header.
touch components/Header.jsx
In your text editor, add the following code to your Header component.
// components/Header.jsx
export const Header = ({ siteConfig }) => {
const headerObjectId = siteConfig.__id + ':header';
return (
<header className="header outer" data-sb-field-path={headerObjectId}>
<div className="inner">
<span className="header-title" data-sb-field-path=".title">{siteConfig.header.title}</span>
<span className="header-nav" data-sb-field-path=".navLinks">
{siteConfig.header.navLinks.map((navLink, idx) => (
<a href={`${navLink.url}`} data-sb-field-path={`.${idx}`}>{navLink.label}</a>
))}
</span>
</div>
</header>
);
};
This will render a header with the site title and navigation links to pages. The data-sb-field-path
attribute is used to enable inline editing for the elements in the header.
Next, edit SiteConfig.js
in the .stackbit/models
directory, to make the Header component accessible to all pages of the website.
// .stackbit/models/SiteConfig.js
module.exports = {
type: 'data',
label: 'Site Config',
singleInstance: true,
fields: [
{ type: 'string', name: 'title', label: 'Site Title' },
{
type: 'model',
name: 'footer',
label: 'Footer Config',
models: ['FooterConfig'],
},
{
type: 'model',
name: 'header',
label: 'Header Config',
models: ['HeaderConfig'],
},
],
};
Import your Header model into the stackbit.config.js
file in the root of your my-stackbit-site
directory. This is the main configuration file for Stackbit.
// stackbit.config.js
import Button from './.stackbit/models/Button';
import Card from './.stackbit/models/Card';
import Page from './.stackbit/models/Page';
import CardGridSection from './.stackbit/models/CardGridSection';
import FooterConfig from './.stackbit/models/FooterConfig';
import HeroSection from './.stackbit/models/HeroSection';
import SiteConfig from './.stackbit/models/SiteConfig';
import HeaderConfig from './.stackbit/models/HeaderConfig';
const sbConfig = {
stackbitVersion: '~0.6.0',
ssgName: 'nextjs',
cmsName: 'git',
nodeVersion: '16',
dataDir: 'content/data',
pagesDir: 'content/pages',
pageLayoutKey: 'type',
assets: {
referenceType: 'static',
staticDir: 'public',
uploadDir: 'images',
publicPath: '/',
},
models: {
Page,
Button,
Card,
CardGridSection,
FooterConfig,
HeroSection,
SiteConfig,
HeaderConfig,
},
};
export default sbConfig;
Add sample data to appear on your Header using the content/data/config.json
file. Give your site the title
: “Medusa + Stackbit” and add the navLinks
for Home
and the Store
page.
{
"type": "SiteConfig",
"title": "Stackbit",
"footer": {
"type": "FooterConfig",
"body": "Made by [Stackbit](https://www.stackbit.com/)\n"
},
"header": {
"type": "HeaderConfig",
"title": "Medusa + Stackbit",
"navLinks": [
{
"label": "Home",
"url": "/"
},
{
"label": "Store",
"url": "/store"
}
]
}
}
Update the pages/[[...slug]].js
file to render the Header when you load your Stackbit site.
// pages/[[...slug]].js
import Head from 'next/head';
import { DynamicComponent } from '../components/DynamicComponent';
import { Footer } from '../components/Footer';
import { Header } from '../components/Header';
import { pagesByType, siteConfig, urlToContent } from '../utils/content';
const FlexiblePage = ({ page, siteConfig }) => {
return (
<div className="page">
<Head>
<title>{page.title}</title>
</Head>
<Header siteConfig={siteConfig} />
<div data-sb-object-id={page.__id}>
{page.sections?.length > 0 && (
<div data-sb-field-path="sections">
{page.sections.map((section, index) => (
<DynamicComponent
key={index}
{...section}
data-sb-field-path={`.${index}`}
/>
))}
</div>
)}
</div>
<Footer siteConfig={siteConfig} />
</div>
);
};
export default FlexiblePage;
export function getStaticProps({ params }) {
const url = '/' + (params.slug || []).join('/');
return { props: { page: urlToContent(url), siteConfig: siteConfig() } };
}
export function getStaticPaths() {
const pages = pagesByType('Page');
return {
paths: Object.keys(pages),
fallback: false,
};
}
Add some styling to your header by adding the following lines at the end of styles/styles.css
.
/* ---- Header ---- */
.header {
background: var(--color-blue);
font-size: 1.5rem;
font-weight: 800;
color: var(--color-white);
padding: 2rem 0rem;
}
.header-nav {
float: right;
}
.header-nav a {
padding-right: 1rem;
}
.header-nav a:hover {
color: var(--color-yellow);
}
Test out your header page. It should be similar to this:
Edit Hero Section
Now, that you have seen how to set up an editable component for your content editor, the next step is to edit the Hero section to see inline editing in action.
Change the message “Welcome to Stackbit” to “Welcome to Medusa + Stackbit!” in the Hero section.
This is the ease with which you can visually edit the content on your site without tinkering with any code.
Add Store Page
Next, let’s add a store page to retrieve and display products from our Medusa ecommerce store. Notice that the Store
link in the header of your site does not work since we have yet to create a Store
page.
Click on the Add New Page
(white and blue +
) icon at the top of your visual editor. Give your page the title Store
and Slug /store
and save by clicking + Add
. This will create a blank page with the title Store.
If you now click Store
on the header it will navigate to the blank page.
Add Model for AllProducts
To add functionality to your Store
page, you will need to create a model called AllProducts.js
in the .stackbit/models
directory. This provides the structure for the content to be displayed on the Store
page.
In your terminal create AllProducts.js
.
touch .stackbit/models/AllProducts.js
In your text editor, add the following code to AllProducts.js
// stackbit/models/AllProducts.js
module.exports = {
label: 'All Products',
groups: ['SectionComponents'],
fields: [
{ type: 'string', name: 'heading', default: 'All Products' },
{
name: 'products',
type: 'list',
items: {
type: 'object',
fields: [
{ type: 'string', name: 'name' },
{ type: 'string', name: 'id' },
{ type: 'string', name: 'description' },
{ type: 'string', name: 'price' },
],
},
},
],
}
For simplicity, the AllProducts model has two fields of interest: a heading
for the Store
page heading and array of products
.
Import the AllProducts.js
model you just created into your stackbit.config.js
file. The updated stackbit.config.js
file should be as follows:
// stackbit.config.js
import Button from './.stackbit/models/Button';
import Card from './.stackbit/models/Card';
import Page from './.stackbit/models/Page';
import CardGridSection from './.stackbit/models/CardGridSection';
import FooterConfig from './.stackbit/models/FooterConfig';
import HeroSection from './.stackbit/models/HeroSection';
import SiteConfig from './.stackbit/models/SiteConfig';
import AllProducts from './.stackbit/models/AllProducts';
import HeaderConfig from './.stackbit/models/HeaderConfig';
const sbConfig = {
stackbitVersion: '~0.6.0',
ssgName: 'nextjs',
cmsName: 'git',
nodeVersion: '16',
dataDir: 'content/data',
pagesDir: 'content/pages',
pageLayoutKey: 'type',
assets: {
referenceType: 'static',
staticDir: 'public',
uploadDir: 'images',
publicPath: '/',
},
models: {
Page,
Button,
Card,
CardGridSection,
FooterConfig,
HeroSection,
SiteConfig,
AllProducts,
HeaderConfig,
},
};
export default sbConfig;
Next, create an AllProducts
component that uses the AllProducts
model in the components
folder. This component will fetch all the products from the Medusa Server and display them on the Store
page.
touch components/AllProducts.jsx
Add the following code to AllProducts.jsx
// components/AllProducts.jsx
import { useState, useEffect } from "react";
import Link from 'next/link';
export const AllProducts = (props) => {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchProducts = async () => {
const response = await fetch('http://localhost:9000/store/products');
const data = await response.json();
setProducts(data.products);
};
fetchProducts();
}, []);
return (
<div data-sb-field-path={props['data-sb-field-path']}>
<div className="outer heading-box">
<div className="inner heading-box">
<h1 className="store-heading" data-sb-field-path=".heading">{props.heading}</h1>
</div>
</div>
<div className="outer store-box">
<div className="inner store-box">
<ul className="store-list">
{products.map((product) => (
<li key={product.id}>
<Link href={`/store/${product.id}`}>
<h2>{product.title}</h2>
<img src={`${product.thumbnail}`} width="250"></img>
<p>${product.variants[0].prices[0].amount}</p>
</Link>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
This component retrieves the products from the Medusa Server at localhost:9000/store/products
and updates the products
state variable with the fetched products. The products are then rendered with a link to each individual product’s page which we will need to create.
To make the AllProducts
component function, you must import it into the DynamicComponent
component. Update DynamicComponent
as follows:
// components/DynamicComponent.jsx
import { HeroSection } from './HeroSection';
import { CardGridSection } from './CardGridSection';
import { AllProducts } from './AllProducts';
const componentsMap = {
HeroSection: HeroSection,
CardGridSection: CardGridSection,
AllProducts: AllProducts,
};
export const DynamicComponent = (props) => {
if (!props.type) {
const propsOutput = JSON.stringify(props, null, 2);
throw new Error(`Object does not have a 'type' property: ${propsOutput}`);
}
const Component = componentsMap[props.type];
if (!Component) {
throw new Error(`No component is registered for type:'${props.type}`);
}
return <Component {...props} />;
};
Add the styling for the Store
page. Add the following lines at the end of styles/style.css
.
/* ---- Store ---- */
.outer.heading-box {
background-image: url("/bg.svg");
background-repeat: repeat;
background-color: rgb(193, 189, 189);
padding: 5rem 0rem;
}
.inner.heading-box {
text-align: center;
font-size: 2.4rem;
font-weight: 800;
color: rgb(4, 4, 83);
}
.inner.store-box {
text-align: center;
padding: 1rem 0rem;
}
ul.store-list {
list-style: none;
}
ul.store-list p {
font-size: 1.5rem;
color: darkblue;
}
Now click on the Store
page link on the site header. You should see a list of all products from the Medusa Server.
The Store
page loads well with a list of all products. However, clicking on each product gives a 404
Error.
Add Single Product Page
To create individual product pages for each product, we will use Next.js’s dynamic routing feature.
Create a new directory named store
in the pages
directory of your my-stackbit-site
folder. In that directory create a new page file called [productId].jsx
. This will be a template for the individual product pages.
mkdir pages/store && touch pages/store/[productId].jsx
Add the following code to productId.jsx
:
// pages/store/[productId].jsx
import { useRouter } from 'next/router';
import { useState, useEffect } from 'react';
const ProductPage = () => {
const router = useRouter();
const { productId } = router.query;
const [product, setProduct] = useState(null);
useEffect(() => {
const fetchProduct = async () => {
const response = await fetch(`http://localhost:9000/store/products/${productId}`);
const data = await response.json();
setProduct(data.product);
};
fetchProduct();
}, [productId]);
if (!product) {
return <div>Loading...</div>;
}
return (
<div className="outer product-box">
<div className="inner product-box">
<h1>{product.title}</h1>
<img src={`${product.thumbnail}`} width="320"></img>
<p className="description">{product.description}</p>
<p className="price">${product.variants[0].prices[0].amount}</p>
</div>
</div>
);
};
export default ProductPage;
The useRouter
hook is used to get the id
of the product from the URL, then uses that id
to fetch data for the individual product. When a user clicks on a product link on the Store
page, they will be taken to an individual product page with the corresponding data fetched from the Medusa server.
Next, add some styling to your ProductPage
by adding the following CSS code at the end of the styles/style.css
file.
/*---- Individual Product ----*/
.inner.product-box {
text-align: center;
padding: 3rem 1rem;
}
.price {
color: darkblue;
font-size: 1.5rem;
}
Preview the product
page by clicking on one of the products in the Store
page.
Add Cart
The Medusa backend can handle the creation and storage of the cart, the retrieval of the cart, adding, updating, and deleting line items from the cart. This frees frontend developers from having to write lots of logic to manage the state of your cart. Check out How to Add Cart functionality | Medusa to learn more about carts in Medusa.
For this tutorial, we will just be implementing the creation of the cart and adding line items to the cart.
First, add a link to the Cart
in the header by updating the content/data/config.json
file.
{
"type": "SiteConfig",
"title": "Stackbit",
"footer": {
"type": "FooterConfig",
"body": "Made by [Stackbit](https://www.stackbit.com/)\n"
},
"header": {
"type": "HeaderConfig",
"title": "Medusa + Stackbit",
"navLinks": [
{
"label": "Home",
"url": "/"
},
{
"label": "Store",
"url": "/store"
},
{
"label": "Cart",
"url": "/cart"
}
]
}
}
If you refresh your site, you should see a link to the Cart
in the page header.
Next, create the Cart
page which will create a new cart or retrieve a pre-existing cart from localStorage
, display the list of products in the cart, and has an addProductToCart
function for adding new line items to the cart.
Create a new file named cart.js
in the pages
folder.
touch pages/cart.js
Add the following code to pages/cart.js
:
// pages/cart.js
import React, { useState, useEffect } from "react";
export default function Cart() {
const [cart, setCart] = useState(null);
useEffect(() => {
const id = localStorage.getItem("cart_id");
if (id) {
fetch(`http://localhost:9000/store/carts/${id}`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ cart }) => setCart(cart))
} else {
fetch(`http://localhost:9000/store/carts`, {
method: "POST",
credentials: "include",
})
.then((response) => response.json())
.then(({ cart }) => {
setCart(cart);
localStorage.setItem("cart_id", cart.id);
});
}
}, []);
if (!cart) {
return <div>Loading Cart...</div>;
}
return (
<>
<div className="outer cart-box">
<div className="inner cart-box">
<h2>Your Cart</h2>
<table>
<thead>
<tr>
<th>Item</th>
<th>Unit Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
{cart.items.map((item) => (
<tr key={item.id}>
<td>{item.title}</td>
<td>${item.unit_price}</td>
<td>{item.quantity}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</>
);
};
export const addProductToCart = (variant_id) => {
const id = localStorage.getItem("cart_id");
if (id) {
fetch(`http://localhost:9000/store/carts/${id}/line-items`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
variant_id,
quantity: 1,
}),
})
.then((response) => response.json())
.then(({ cart }) => console.log(cart));
}
};
This is a simple cart to illustrate the power of Medusa’s ecommerce backend. The useEffect
is used to fetch a cart using the id
of the cart, if it doesn’t exist it will create one and parse it to cart
array. The cart is then rendered with the list of products.
Next, add some styling to the cart
page by adding the following code at the end of styles/style.css
/*---- Cart ----*/
.outer.cart-box {
padding: 2rem 1rem;
}
.cart-box li {
list-style: none;
padding-bottom: 1rem;
}
/*---- Table ----*/
table {
border-collapse: collapse;
border: 3px solid rgb(200,200,200);
letter-spacing: 1.5px;
font-size: 1rem;
}
td, th {
border: 1px solid rgb(190,190,190);
padding: 10px 20px;
}
th {
background-color: rgb(235,235,235);
}
td {
text-align: center;
}
tr:nth-child(even) td {
background-color: rgb(250,250,250);
}
tr:nth-child(odd) td {
background-color: rgb(245,245,245);
}
caption {
padding: 10px;
}
To test out the cart, you will need to import the addProductToCart
function into the AllProducts
component.
Update the AllProducts
component with the following code:
// components/AllProducts.jsx
import { useState, useEffect } from "react";
import Link from 'next/link';
import { addProductToCart } from '../pages/cart';
export const AllProducts = (props) => {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchProducts = async () => {
const response = await fetch('http://localhost:9000/store/products');
const data = await response.json();
setProducts(data.products);
};
fetchProducts();
}, []);
return (
<div data-sb-field-path={props['data-sb-field-path']}>
<div className="outer heading-box">
<div className="inner heading-box">
<h1 className="store-heading" data-sb-field-path=".heading">{props.heading}</h1>
</div>
</div>
<div className="outer store-box">
<div className="inner store-box">
<ul className="store-list">
{products.map((product) => (
<li key={product.id}>
<Link href={`/store/${product.id}`}>
<h2>{product.title}</h2>
<img src={`${product.thumbnail}`} width="250"></img>
<p>${product.variants[0].prices[0].amount}</p>
</Link>
<button className="button" onClick={() => {
addProductToCart(product.variants[0].id);
alert('Added to Cart');
}}>Add to Cart</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
We have added an Add to Cart
button which implements the addProductToCart
function for each product. Reload your Store
page, to see the changes take effect.
Click on the Add to Cart
button for any product, then navigate to the Cart
page and you will find the items you just added to your cart list. Here’s an example:
Preview Site
After adding all these features, the next step is to preview your site. Stackbit provides a Preview
option in the Visual Content Editor for you to test out the site.
Feel free to toggle between the Preview
button and the Edit
button and tinker with your site.
Deployment
The next step is the process of deployment. For in-depth guides on deploying the Medusa server use Medusa Server Deployment Guides.
This tutorial did not cover creating a Stackbit cloud project. This is useful when you want to introduce content editors to your site. Check out Create Stackbit from GitHub for more information.
As for deploying your storefront, you can use Vercel or Netlify. Check out these articles:
- How to Deploy Your Medusa Ecommerce Storefront and Admin from a Monorepo using Vercel
- Medusa Storefront Deployment Guides
Conclusion
In this tutorial, we learned how to create an e-commerce storefront using Medusa and Stackbit.
We explored the visual editing experience that Stackbit provides as well as the feature-rich Medusa ecommerce backend.
We added a Store page that displays all products from the Medusa server and added individual product pages for each product using Next.js dynamic routing. We also added a simple cart that displays the list of products. Finally, we previewed the site and discussed deployment options.
As you continue to work with Stackbit and Medusa, there are many additional features and customizations that you can explore. For example, you can add more complex cart and checkout functionality, integrate payment providers and create a custom design for your storefront.
It's important to remember that this tutorial only scratched the surface of what is possible with these powerful tools. However, it should provide you with a solid foundation to build upon and experiment with.
If you run into any issues or have questions, feel free to reference the extensive documentation available for both Stackbit and Medusa or to seek help from the vibrant communities surrounding these tools.
Happy building!
Top comments (1)
Nice tutorial thank you for writing it!