1: Build a UI with BigDesign
1: Build a UI with BigDesign
This exercise runs entirely in MOCK mode — DATA_MODE=MOCK in .env.local, the setting your project already uses from the initial setup. No real API requests are made, and the mock data you need ships with the boilerplate, so you don’t need BigCommerce credentials or a store yet.
By the end of this exercise, your app renders a working gift certificates list and detail page, styled with BigDesign, BigCommerce’s React design system.
Install the BigDesign packages
BigDesign isn’t part of the project boilerplate, so your first task is to add it.
Run the following command in your project directory to install BigDesign and styled-components.
@bigcommerce/big-design provides the components themselves, -theme supplies the design tokens the components read, and -icons is BigDesign’s icon set. styled-components is the CSS-in-JS library BigDesign is built on, so it’s a direct dependency of your app rather than just a transitive one.
styled-components v6 ships its own TypeScript types, so no separate @types/styled-components package is needed.
Wire BigDesign into the root layout
BigDesign is styled-components based, so getting it working in a Next.js App Router project takes a few pieces of setup. In this section, you’ll make the layout modifications that wire BigDesign into the UI.
Enable the styled-components compiler transform
Update next.config.ts to enable the styled-components compiler transform.
Without this setting, styled-components generates class names at runtime in an order that can differ between the server’s render and the client’s first hydration pass.
Create the BigDesign provider
Update src/components/ui/big-design-provider.tsx.
BigDesign components are Client Components
Note the "use client" directive at the top of this file. BigDesign’s components are Client Components: they rely on browser APIs, React state, and styled-components’ runtime, none of which exist during a server render.
In the Next.js App Router, every component is a Server Component by default — rendered on the server, never shipped to the browser, and unable to use hooks or event handlers. "use client" marks the boundary where that changes. A file with the directive, and everything it imports, is bundled for the browser and hydrated there.
BigDesign declares this boundary itself, so you can import its components directly from @bigcommerce/big-design into a Server Component and render them without adding "use client" to your own file. You only need the directive when your component needs client-side behavior of its own — state, effects, or event handlers — as this provider does.
Create the styled-components registry
Update src/components/ui/styled-components-registry.tsx.
Add registry/providers in the root layout
Update src/app/layout.tsx to remove font-related imports and constants.
Update the same file to wire in the styled-components registry, the proper font, the BigDesign provider, and the alerts manager around your app’s children.
StyledComponentsRegistry collects styled-components’ server-rendered styles so they ship with the SSR payload instead of flashing unstyled, and BigDesignProvider supplies the styled-components ThemeProvider (with BigDesign’s theme) plus GlobalStyles. ActionAlertsManager is a single, app-wide manager that renders whatever alert a server action queues from anywhere in the tree.
With these pieces in place, restart your dev server and confirm the app still loads without hydration warnings in the console. Nothing visually changes yet; this section only wires up the machinery the rest of the exercise depends on.
Convert main layout components to BigDesign
A few of the UI primitives already in place in the project’s boilerplate should be updated to use BigDesign components before we proceed with building out the gift certificate pages.
Rebuild the app shell
Open src/components/gift-certs-manager/app-shell.tsx and remove existing imports for UI components.
Update the same file to rebuild it with BigDesign’s layout primitives.
Rebuild the alerts manager
Update src/components/ui/action-alerts.tsx to remove the useSyncExternalStore import and all logic before showSuccessAlert.
Update the same file as shown below.
This component manages alerts for the whole app. The updates replace the custom implementation with a simple render of the existing BigDesign AlertsManager,
as well as implementing the appropriate manager add calls.
Rebuild the app link
Update src/components/ui/app-link.tsx.
Navigation links within the app remain a custom (albeit thin) component. This component combines native Next.js links (exhibiting proper client-side navigation behavior) with style decoration matching BigDesign’s theme.
Rebuild the content fallback
Update the imports in src/components/layout/content-fallback.tsx. This is an important “loading” fallback state when caching and <Suspense> are in use.
Nothing about the component implementation needs to change; Flex and ProgressCircle simply get replaced with BigDesign-native components instead of custom primitives.
Rebuild the data mode banner
Update the imports in src/components/layout/data-mode-banner.tsx. This banner reminds users when the app is running in a non-production mode.
Rebuild the developer info panel
Update the imports in src/components/layout/developer-info-panel.tsx. This side panel identifies the app developer information.
Build the gift certificates list page
You can finally start building out new UI for your gift certificates list page.
You’ll use a component pattern common to all page routes in the example app: an outer route component where an authorization and <Suspense> boundary will eventually live, a page component where dynamic values are resolved, and a cacheable view component where data fetching is handled and the main UI actually begins.
Create the gift certificate table
The table that drives the core gift certificate list UI is its own component - a React client component, due to the involvement of browser interactivity.
Note that the client nature of the component necessitates its separation from the view component, which handles data fetching server-side.
Update src/components/gift-certs-manager/gift-certificates/list/gift-certificate-table.tsx with the table implementation.
This component is purely presentational: it renders the page of items the server already fetched, and every search, sort, or pagination interaction navigates to a new URL with router.push rather than holding its own state or fetching data. The view component you’ll build next reads the resulting querystring with searchParams and re-fetches server-side, and PendingOverlay shows the in-between state.
Note that the BigDesign Table component is doing all the heavy lifting of the UI, requiring only props controlling the displayed columns, items, and nagivation behavior.
Implement the view component
This view component handles data fetching and then hands off rendering to the table component you built in the previous step. The component lives separately from the resolution of dynamic values like querystring params, receiving only serializable props so it remains cacheable.
Update src/components/gift-certs-manager/gift-certificates/list/gift-certificate-list-view.tsx.
Note that the component accepts a storeHash prop. While this isn’t currently used, it’s critical to the eventual single-click app implementation, where all app
URLs will include a /store/<store-hash> segment identifying the store context.
The data fetching is done with the fetchGiftCertificates function that already exists in the project boilerplate. In this exercise, we’re concerned only with UI and not
the data access layer. fetchGiftCertificates currently returns mock data. parseGiftCertificatesQuery turns the raw querystring params into a normalized query object; open and examine the import file if you want to see the details.
Complete the page component
Proceeding up the component tree, the page component is a quite thin layer, responsible for resolving params and searchParams and passing dynamic values to the view component as serializable props.
Update src/components/gift-certs-manager/gift-certificates/list/gift-certificates-page.tsx.
Complete the route component
At the top of the hierarchy is the route component itself. Right now this does nothing but render the page component you built in the previous step. In future exercises, you’ll see that the route will become the home of an authorization check.
Update src/app/store/[storeHash]/gift-certs/page.tsx.
Restart your dev server if needed and browse to /gift-certs to confirm the table renders with mock data, including working sort and pagination.

Replace the store home page with the gift certificates list
Update the home page export
Replace the contents of src/app/store/[storeHash]/page.tsx with a pass-through export to the gift certificates list page you just built.
This step is a convenience, adopting the gift certificate list page as the content of the base route. This convention keeps the example app cleanly separate and easy to swap out for different home page content.
Build the gift certificate detail page
The core component hierarchy (route, page, and view components) will apply to the gift certificate detail page as well. However, the UI will be broken into several smaller components in this case, due to the composition of multiple tabs and panels.
Implement the status panel
The first and smallest piece of UI for the detail page is a “status” panel with basic information about the gift certificate.
Replace the contents of src/components/gift-certs-manager/gift-certificates/detail/gift-certificate-status-panel.tsx with the following contents.
Note that the component expects an onStatusChange handler controlled by the ancestor.
Implement the party panel
A “party” will refer to either the sender or recipient of a gift certificate. This panel will be used to show details of both.
Replace the contents of src/components/gift-certs-manager/gift-certificates/detail/gift-certificate-party-panel.tsx.
Implement the details tab
With both panels implemented, you can now build out the “Details” tab, which displays basic gift certificate, sender, and recipient information.
Replace the contents of src/components/gift-certs-manager/gift-certificates/detail/gift-certificate-details-tab.tsx.
Implement the balance tab
The gift certificate’s balance will be displayed in a tab of its own, which will eventually be dedicated to several actions capable of updating the balance.
Replace the contents of src/components/gift-certs-manager/gift-certificates/detail/gift-certificate-balance-tab.tsx.
Implement the tabs container
This thin client component implements the tabs interface itself, using the two tab components you’ve built.
Replace the contents of src/components/gift-certs-manager/gift-certificates/detail/gift-certificate-tabs.tsx.
As with the table on the gift certificate list page, you’re letting the BigDesign Tabs component do the heavy lifting, using a piece of
React state (activeTab) to track the currently selected tab.
Complete the view component
From here, the component hierarchy mirrors the list page. The view component performs data fetching and hands off to GiftCertificateTabs for the main UI.
Update src/components/gift-certs-manager/gift-certificates/detail/gift-certificate-view.tsx.
As with the list page, fetchGiftCertificate is an existing data access layer function that currently returns mock data.
Complete the page component
The page component is also familiar, resolving dynamic params and passing them as props to the view component.
Update src/components/gift-certs-manager/gift-certificates/detail/gift-certificate-detail-page.tsx.
Note that both id and storeHash are resolved from params.
Complete the route component
Update src/app/store/[storeHash]/gift-certs/[id]/page.tsx.
The props that the route passes directly to GiftCertificateDetailPage contain the params where that component looks for id
and storeHash. These are resolved from the [id] and [storeHash] route segments in the file path of the route.
Browse to a gift certificate from the list you built earlier in this exercise, and confirm both tabs render.

Status update and balance refill actions
This step will give you a chance to incorporate BigDesign components and patterns for user actions. The actions we’ll implement for the scope of this tutorial include updating a gift certificate’s status and refilling its balance.
Implement the server actions
The client components that drive user interactivity on the gift certificate detail page directly call server actions - functions that execute on the server.
Update src/app/store/[storeHash]/gift-certs/[id]/actions.ts.
Note that the two server action functions accept simple arguments and return simple JSON results; Next.js takes care of wrapping the function execution in a web request from the client components.
Both actions re-fetch the certificate server-side before acting on it, trusting only the id the client supplied. Once again, you’re relying on
data access layer functions (like refillGiftCertificateBalanceRequest) that already exist; you’re not yet concerned with the API call details.
On success, each action calls revalidatePath for both the detail and list routes. Without this, the mutation would succeed on the server but the
user would keep seeing the stale values Next.js already rendered, making the action look broken. Invalidating both routes means the certificate’s
new status or balance is reflected whether the user stays on the detail page or navigates back to the list.
Add refill behavior to the balance tab
Modify src/components/gift-certs-manager/gift-certificates/detail/gift-certificate-balance-tab.tsx to add the refill action, a confirmation modal, and a pending state.
canRefill is a pre-existing helper in the boilerplate’s gift-certificates/status.ts. It centralizes the conditions under which a refill is
offered at all — currently, that the certificate’s status is one that can still be refilled. Using it here rather than an inline status check keeps
the button’s enabled state and the server action’s own validation from drifting apart. The separate canSubmitRefill check then gates the submit
button on the entered amount actually exceeding the current balance, so the action can’t be sent in a state the server would just reject.
On a gift certificate detail page, you should now be able to observe the UI interactions for status updates and balance refills. NOTE, however, that updates will currently fail. The static mock data you’re currently relying on can’t be mutated! You’ll be revisiting the updating logic in the next exercise.

Our basic BigDesign-based UI for managing gift certificates is now complete!
Convert remaining components to BigDesign
The initial project boilerplate still contains a few components built with custom UI not yet converted to BigDesign. These mostly consist of error pages. After converting these, several custom UI components can finally be deleted.
We won’t walk through these final conversions explicitly, but see the example code link below if you want to clean up these final components.