Learn about the Storefront Next project structure to navigate code quickly and follow conventions that keep your codebase maintainable as it grows.
Overview
A Storefront Next project is built on React Router 7 in framework mode, and uses Vite as the build tool. The structure separates your app code (src/) from configuration files at the project root.
my-storefront/
├── src/ # Application code
│ ├── routes/ # Page components (file-based routing)
│ ├── components/ # Reusable UI components
│ ├── lib/ # Utilities and API clients
│ ├── hooks/ # Custom React hooks
│ ├── providers/ # React context providers
│ ├── middlewares/ # Request/response middleware
│ ├── locales/ # Translation files
│ ├── root.tsx # Application shell and layout
│ ├── routes.ts # Route configuration
│ └── app.css # Global styles (Tailwind)
├── public/ # Static assets (images, fonts)
├── types/ # TypeScript type definitions
├── config.server.ts # Commerce API and site configuration
├── vite.config.ts # Build configuration
├── react-router.config.ts # React Router settings
└── package.json # Dependencies and scripts
Application Code in src/
Routes
The routes/ directory contains your page components. Storefront Next uses React Router's file-based routing, where file names determine URL paths.
src/routes/
├── _index.tsx # Home page (/)
├── category.$.tsx # Category pages (/category/*)
├── product.$productId.tsx # Product pages (/product/:productId)
├── cart.tsx # Cart page (/cart)
├── checkout.tsx # Checkout (/checkout)
├── account.tsx # Account layout
├── account._index.tsx # Account home (/account)
├── account.orders.tsx # Order history (/account/orders)
└── action.cart-item-add.tsx # Server action for adding to cart
File Naming Conventions
| Pattern | Example | URL | Purpose |
|---|---|---|---|
_index.tsx |
_index.tsx |
/ |
Index route (renders at parent path) |
name.tsx |
cart.tsx |
/cart |
Static route |
$param |
product.$productId.tsx |
/product/:productId |
Dynamic segment that captures a value |
$.tsx |
category.$.tsx |
/category/* |
Splat/catch-all route |
parent.child.tsx |
account.orders.tsx |
/account/orders |
Nested route |
action.* |
action.cart-item-add.tsx |
— | Server action (not a navigable page) |
note: Files starting with underscore (_) have special meaning. _index.tsx renders at the parent path, while a _layout.tsx file creates a layout wrapper without adding a URL segment.
The routes.ts file configures routing behavior. The flatRoutes() function scans your src/routes/ directory and generates route configuration automatically based on file names. You rarely have to modify this file.
// src/routes.ts
import { type RouteConfig } from '@react-router/dev/routes';
import { flatRoutes } from '@react-router/fs-routes';
export default flatRoutes() satisfies RouteConfig;
<!-- For more details on routing patterns, see [Routing](sfnext-routing.md). -->
Components
The components/ directory contains reusable UI elements organized by feature area.
src/components/
├── ui/ # Base UI primitives (buttons, inputs, dialogs)
├── header/ # Site header and navigation
├── footer/ # Site footer
├── product-view/ # Product display components
├── product-tile/ # Product cards for listings
├── cart/ # Cart components
└── ...
The ui/ subdirectory contains shadcn/ui components—accessible, customizable primitives that you can modify directly.
Create a component when:
- The UI element is used in multiple places.
- The component encapsulates complex logic or state.
- You want to test the UI in isolation (via Storybook).
Lib
The lib/ directory contains utilities, API clients, and business logic that isn't tied to React.
src/lib/
├── api/ # Commerce API client functions
├── utils/ # General utilities (formatting, validation)
└── ...
Place code here when it:
- Doesn't use React hooks or components.
- Could be shared across multiple routes or components.
- Handles data transformation or business logic.
Hooks
Custom React hooks extract reusable stateful logic from components.
src/hooks/
├── use-product.ts # Product data access
├── use-cart.ts # Cart operations
├── use-navigation.ts # Navigation utilities
└── ...
When to create a custom hook:
- Multiple components need the same stateful logic.
- You want to encapsulate complex operations (like API calls with loading states).
- You must share behavior across routes.
Providers
React context providers supply data and functionality throughout the app.
src/providers/
├── cart-provider.tsx # Cart state and operations
├── locale-provider.tsx # Internationalization context
└── ...
Providers wrap the app in root.tsx and make data available to any nested component without prop drilling.
Middlewares
Request and response middleware that runs before route loaders:
src/middlewares/
├── auth-middleware.server.ts # Authentication (server-side)
├── auth-middleware.client.ts # Authentication (client-side)
├── i18n-middleware.ts # Locale detection and setup
└── ...
Middleware is registered in root.tsx and executes in order for each request. Use middleware to:
- Parse authentication tokens
- Detect user locale
- Set up shared context that loaders need
For details on implementing middleware, see Storage and Sessions.
Locales
Translation files for internationalization:
src/locales/
├── en-US.json
├── fr-FR.json
└── ...
See Internationalization for setup and usage.
Root File (root.tsx)
The root.tsx file is your app's entry point. It exports several key pieces that define your app's behavior:
| Export | Purpose |
|---|---|
middleware |
Server middleware chain (auth, locale detection) |
clientMiddleware |
Client middleware chain (runs during client navigation) |
loader |
Root data loader (fetches data needed by all pages) |
Layout |
HTML document structure (<html>, <head>, <body>) |
default (App) |
Application shell with header, footer, and <Outlet /> for page content |
Most customization happens in middleware and the App component. Typically, you only need to modify Layout when changing document-level meta tags or adding scripts.
Configuration Files
| File | Purpose |
|---|---|
config.server.ts |
Commerce API connection and storefront settings. See Configuration. |
vite.config.ts |
Build plugins, path aliases, Server-Side Rendering (SSR) settings. See Build Tools. |
react-router.config.ts |
React Router framework options |
tsconfig.json |
TypeScript compiler options |
package.json |
Dependencies and npm scripts |
Where to Put Code Elements
| You want to... | Put it in... |
|---|---|
| Add a new page | src/routes/ (file name = URL path) |
| Create a reusable component | src/components/ |
| Add a server action | src/routes/action.*.tsx |
| Add a utility function | src/lib/ |
| Create a custom hook | src/hooks/ |
| Add translations | src/locales/ |
| Add static assets (images) | public/ |
| Add global styles | src/app.css |
| Configure commerce or storefront settings | config.server.ts |
Example: Adding a Wishlist Feature
Here's how the project structure guides you when adding a new feature:
src/
├── routes/
│ ├── wishlist.tsx # 1. Page at /wishlist
│ └── action.wishlist-add.tsx # 2. Server action for adding items
├── components/
│ └── wishlist/
│ ├── wishlist-item.tsx # 3. UI components
│ └── wishlist-empty.tsx
├── hooks/
│ └── use-wishlist.ts # 4. Hook for wishlist operations
└── lib/
└── api/
└── wishlist.ts # 5. API client functions
This separation keeps your code organized.
- Routes define what URLs exist and load data.
- Components handle presentation.
- Hooks manage stateful logic.
- Lib contains pure functions and API calls.
Customizing Entry Files
React Router provides default entry files for client hydration and server rendering. These entry files are hidden by default but can be revealed for advanced customization.
npx react-router reveal
This command creates entry.client.tsx and entry.server.tsx in your src/ directory.
When to customize:
- Initialize client libraries before hydration
- Add custom error reporting (for example, Sentry)
- Modify server response headers
- Customize streaming behavior
For most projects, the default entry files work without modification.