Storefronts

Design a shop in PopCraft and publish it as a store that sells — a Next.js Commerce store on Vercel with your Shopify products, cart and checkout — or export it for a Next.js Commerce store. The Ecommerce plugin adds the Shopify catalogue, store setup and a Shopify theme.

Design a shop once, bound to a store's products, cart and menu, and publish it as a store that sells, or export it to whichever store runs it. Export storefront… has two targets, and plugins add more (the Ecommerce plugin adds a Shopify theme):

TargetWhat you getWhere it runs
Next.js Commerce storeThe whole store: Next.js Commerce with a page per screen, drawn by the designVercel, or any Next.js host, with a Shopify store's domain and Storefront token
Next.js Commerce componentsA React component per page, its props the store's data and server actionsA Next.js Commerce store you already run

The store keeps what it already does well (the catalogue, the cart, checkout, search); PopCraft supplies the design: its layout at every breakpoint, its type, colours and motion. A designer changes the shop in PopCraft, exports it again, and the store picks the new design up without anyone rewriting a page.

Export a storefront as components for Next.js Commerce · 0:06
Description

The Fieldwork storefront template on the canvas: its home, search, product and cart pages, drawn with the shop's sample products. The command palette runs Export as storefront components… and a zip downloads with a React component per page — HomePage, SearchPage, ProductPage and CartPage — whose props are the store's products, cart and menu and its add-to-cart and checkout actions.

Publish a store

A storefront design publishes as a store that sells. Open Publish (or press ⌘K and run Publish as a store): the Store section says which Shopify store it sells from and where it is hosted, and Deploy store sends it.

  1. Choose the Shopify store in the Ecommerce plugin. It keeps the store's address and public Storefront token in the file's site environment (SHOPIFY_STORE_DOMAIN, SHOPIFY_STOREFRONT_ACCESS_TOKEN).
  2. Connect Vercel in Integrations.
  3. Deploy store. PopCraft builds the whole Next.js Commerce store from the design, makes a Vercel project for it (the first time), sets the site environment as the project's encrypted environment, and deploys it. It is live in a minute or two, at the project's address.

The store is Next.js Commerce: the products, prices and pictures are your Shopify store's, add to cart and the cart are real, and Check out goes to Shopify's checkout. Its menu is your store's main menu (Online Store › Navigation), or every product and its first categories when there is none. Change the design and deploy again: the store picks up the new design. Add your own domain to the project in Vercel.

The Ecommerce plugin

Shopify is not built into PopCraft: the Ecommerce plugin brings it, from Marketplace › Plugins. It opens as a tab on the side of the editor:

  • Store. Your Shopify store's .myshopify.com address and its public Storefront access token (Shopify admin › Sales channels › Headless › your storefront › Storefront API). It keeps them in the file, so publishing and everyone who opens the file use the same store.
  • Catalogue. The store's products as pictures, filtered by category, a product or a search, sorted and limited. Select layers on the page and click a product to link them to it: they show that product (its picture, title and price, and a product card's properties), and stay linked to it when it changes in your store. + places just its picture. Use in the design fills the design's products with the ones shown, so the canvas shows what the store really sells; products already in the design keep their place, so linked layers stay linked.
  • Make it a store. Adds the products, categories, cart, cart lines and menu collections a store needs, with sample records, to any design.
  • Checklist. What the design still needs to sell (a product list, a product page, add to cart, the cart, checkout), each with the layer to fix selected for you, and Fix where the plugin can do it: a store form gets the fields it sends without your adding hidden fields by hand.
  • Shopify theme. Export › Shopify theme writes an Online Store 2.0 theme of the design to upload in your store's admin (Online Store › Themes › Add theme), running on Shopify with its own cart and checkout.

Start from a storefront template

Nine templates are whole stores, each in a world of its own, their pages matching a store's routes:

TemplateThe storeHow it opens and moves
Fieldwork storefrontWorkwear: light, a split heroRises in, calm
Kiln storefrontA ceramics studio: a serif, a full-width studio pictureRises in, calm
Ode product launchOne kettle made the hero, dark and quietRises in; its accessories and specs under it
Static streetwear dropBlack and acid lime, a poster over a night streetA ticker for the drop; pops, slides and bounces
Bloom skincare storeBlush and plum, a soft serif, framed cardsIts products float over a centred hero; blurs in
Summit outdoor storeA mountain poster, condensed typeA ticker for the sale; drops and pops in
Marginalia bookshopCream and ink, a literary serif, tall coversThe headline types itself; the picture wipes in
Greenhouse plant shopSage, round framed cardsIts plants float over a centred hero; blurs in
Circuit audio storeDark navy and electric blue, mono detailsPops in; its pictures drift
ScreenRouteWhat it shows
Home/The hero, three featured products, the store's story, a carousel of every product, what is new, the newsletter (a launch: the product, bought from there)
Search/searchThe search field, the categories and every product
Category/search/[handle]One category: its title and words, the others as chips, its products
Product/product/[handle]One product: its picture, price, description, add to cart, and related products
Cart/cartA line per product (quantity, price, remove), the totals and checkout

Their products, categories, cart and menu are collections with a commerce schema. On the canvas they show the shop's sample products, or the Ecommerce plugin's real ones from your store. Exported, each becomes a prop the store fills with its own data.

Bind your own design

Any design can be a storefront. You need three things:

  1. A collection with a schema. In Collections, make a collection and set Stands for:

    • commerce.product: title, handle, URL, description, price, max price, image, image alt, available, variant ID and tags.
    • commerce.collection: title, handle, URL and description.
    • commerce.cartLine: title, variant, URL, image, quantity, price and merchandise ID.
    • commerce.cart: count, subtotal, taxes, total and checkout URL. It has one record, shown through a list of one.
    • commerce.menu: title and URL.

    The schema's fields are made for you; add sample records to design with, or fill it with your store's products in the Ecommerce plugin, whose Make it a store makes all five at once.

  2. Lists and picked products. A frame that repeats over the collection draws its words and pictures from each record. A layer can instead show one product you pick in the Design panel's Data section (or by clicking it in the Ecommerce plugin): published, it shows the store's product with that handle. What a layer shows of its product is bound in Data: a text's words, a picture, or a component instance's properties. The templates' cards are a Product card component whose Image, Title and Price are bound to the product's image, title and price. A screen whose page is one per record of a product collection is a product's page. A link from a card to that screen goes to the product it is drawn for.

  3. Forms that do a store's job. Set a form's Sending it to a storefront action:

    ActionWhat the form sendsThe prop
    Adds the product to the cartmerchandiseId (a hidden field, {{item.variant_id}})onAddToCart
    Changes a cart line's quantitymerchandiseId and quantityonUpdateCartLine
    Removes a line from the cartmerchandiseId ({{item.merchandise_id}})onRemoveCartLine
    Goes to checkoutnothingonCheckout
    Searches the storeqonSearch

Next.js Commerce components

What is in the zip

PathWhat it is
components/popcraft/HomePage.tsx, ProductPage.tsx, …A component per screen, named for it. Its props are the lists it draws, the record it is about, and the forms it sends
components/popcraft/index.tsEvery component and its props type
public/popcraft/assets/The pictures the design draws itself; product images come from the store

A component looks like this:

export interface ProductPageProps {
  /** The product this page is about. */
  product?: Product
  /** Products (the related products under it). */
  products?: Product[]
  cart?: Cart
  menu?: Menu[]
  onAddToCart?: (formData: FormData) => void | Promise<void>
}
 
export function ProductPage({ product, cart, products, menu, onAddToCart }: ProductPageProps = {}) { … }

A list's prop is named for its collection (products, cartLines); two lists of one collection share it (the home page's featured and new products both draw from products, each its own slice). The types are Next.js Commerce's own (Product, Cart, CartItem, Collection, Menu), from @popcraft/runtime/commerce, so a store passes its data in unchanged. With no data, a component draws the design's sample records, so a component renders on its own in a storybook or a test. Left out means samples, so a store passes what it has even when it is empty: cartLines={[]}, and an empty cart rather than none.

Use them in a Next.js Commerce store

Install the runtime, export the components into the store and render them from its pages:

npm install @popcraft/runtime
// app/product/[handle]/page.tsx
import { getProduct, getProductRecommendations, getCart, getMenu } from 'lib/shopify'
import { addItem } from 'components/cart/actions'
import { ProductPage } from 'components/popcraft'
 
export default async function Page({ params }: { params: Promise<{ handle: string }> }) {
  const { handle } = await params
  const product = await getProduct(handle)
  if (!product) return notFound()
  const [related, cart, menu] = await Promise.all([getProductRecommendations(product.id), getCart(), getMenu('next-js-frontend-footer-menu')])
  async function add(formData: FormData) {
    'use server'
    await addItem(null, String(formData.get('merchandiseId')))
  }
  return <ProductPage product={product} products={related} cart={cart} menu={menu} onAddToCart={add} />
}

getpopcraft/examples › commerce is a whole store made this way: Next.js Commerce with its pages rendering a PopCraft design, and npm run design:sync to export again.

From the command line

popcraft storefront shop.popcraft --target next-commerce --out .           # components, into a Next.js Commerce store
popcraft storefront shop.popcraft --target next-commerce-store --out shop  # the whole store

--page picks the page, --styling tailwind writes Tailwind classes and --dir another folder for the components. From the CLI, pictures only the canvas can draw (a shader, a blur) are left as a comment; export from the app to have them drawn.

More targets from plugins

A plugin can write a store PopCraft does not: BigCommerce's Stencil, WooCommerce, a headless CMS. It contributes an exporter and reads the open page's storefront with popcraft.getStorefront(): each page's HTML and stylesheet with the store's data marked rather than filled, and the collections it reads.

const store = await popcraft.getStorefront()
// store.pages[i].html: the page, its fields marked {{page.title}} (the record a page is about) and {{item.price}}
// (inside a list), each list between <!-- pc:each list="Products" schema="commerce.product" as="item" limit="4" -->
// and <!-- pc:end -->, each storefront form with data-pc-action="addToCart" (removeCartLine, checkout, search…)
// store.collections: each with its schema, its fields' keys and its sample records
const files = toMyStoreTemplates(store)  // your platform's templates
await popcraft.saveExport({ name: 'theme.zip', bytes: zip(files) })

The fields' keys are the same in every target: title, price, image, url, variant_id for a product, and so on for the cart, its lines, categories and the menu.

What stays with the store

Variant pickers, the cart's optimistic updates, search's sorting and filtering, accounts and checkout are the store's, in every target. The design draws the first available variant's ID into its add-to-cart form; a store that sells options renders Next.js Commerce's variant selector beside the component, or binds its own.