Nitrogen is a Nuxt template inspired by Shopify's Hydrogen framework for headless commerce. This template is designed to empower Nuxt developers to build fast, scalable, and customizable storefronts that incorporate key features from Hydrogen's starter theme.
Important
This template now features a minimal Sanity studio template, which synchronizes content between a Sanity dataset and your Shopify storefront. This allows teams to further enhance product and collection pages with custom modules or curated links — anything, really.
- 💪 Strongly typed
- 🛒 Cart functionality
- 🔒 User authentication, with password reset
- 👤 Full customer account functionality
- 🗂️ Collection pages, with pagination
- 🕹️ Collection filter and sort functionality
- 👕 Product pages, with metafields
- 🔍 Search functionality
- 🌐 Shop localization
- 💡 Sitemap, with robots
- 📫 Klaviyo integration
- 🎠 Embla Carousel
- 🎨 Tailwind v4
- 🔮 Codegen
Tip
Read through the docs to learn how to configure your Shopify store to work with Nitrogen!
To begin using Nitrogen, you'll need to add the following environment variables:
# Shopify
NUXT_SHOPIFY_DOMAIN=your-shop-name.myshopify.com
NUXT_SHOPIFY_ADMIN_ACCESS_TOKEN=your_admin_access_token
NUXT_SHOPIFY_STOREFRONT_ACCESS_TOKEN=your_storefront_access_token
NUXT_SHOPIFY_API_VERSION=2025-01
# Klaviyo (optional)
NUXT_KLAVIYO_PUBLIC_API_KEY=your_public_api_key
NUXT_KLAVIYO_PRIVATE_API_KEY=your_private_api_key
NUXT_KLAVIYO_API_VERSION=2025-01-15
# Sanity (optional)
NUXT_SANITY_PROJECT_ID=your_project_id
NUXT_SANITY_DATASET=production
NUXT_SANITY_API_VERSION=2025-02-02
NUXT_SANITY_STUDIO_URL=http://your-site-domain.com
NUXT_SANITY_API_READ_TOKEN=your_api_read_tokenWarning
It is strongly recommended that you use the 2025-01 Storefront API version or higher. If not, you will not have access to new API features found within this template (this will cause breaking changes).
- Install dependencies using
pnpm install - Generate your project types using
pnpm codegen - Start the development server using
pnpm dev
Nitrogen features two custom modules for Shopify and Klaviyo, located in the /modules folder. The Shopify module, in particular, lets you connect to both the Storefront API and Admin API at the same time, which is ideal for building complex storefronts that may use Shopify to act a database in some way (think wishlist functionality or unique customer account features).
Tip
Read the official Nuxt Author Module Guide to learn how to create and manage your own modules!
A minimal GraphQL client is provided to seamlessly integrate with both the Storefront API and Admin API. It uses two server-side proxies to handle API authentication and requests, while offering a typed interface for executing GraphQL operations.
The client query function accepts three optional parameters:
api– Choose between thestorefront(default) oradminAPI.maxRetries– Number of retry attempts on failure (default:3).cacheable– Enable response caching for common queries (default:true).
Warning
By default, the GraphQL client only caches collection, product, and search queries. Avoid caching global queries or frequently updated mutations, as this can lead to hydration errors.
This project includes pre-built GraphQL operations for common queries and mutations frequently used in headless storefront environments. All operations are powered by the GraphQL client query, so you can also pass optional parameters when needed:
import type { MyQuery, MyQueryVariables } from '@@/types/storefront'
import { MY_QUERY } from '../graphql/custom'
import { query } from '../utils/graphql-client'
// Fetch example with optional params
const fetchExample = async (
variables: MyQueryVariables,
): Promise<MyQuery['item']> => {
const response = await query(MY_QUERY, variables, { api: 'admin' })
return response.data?.item
}Feel free to add or remove operations that fit your project needs!
To get GraphQL operations, use the useShopify composable:
const shopify = useShopify()Operations can be referenced using dot notation:
// Shopify
const shopify = useShopify()
// With dot notation
await shopify.cart.addLines(cart.id, [ ... ])
await shopify.product.get({ handle: 'example-product' })Perfect for reactive data fetching using useAsyncData:
// Shopify
const shopify = useShopify()
// Fetch Shopify data
const productVars = computed<ProductQueryVariables>(() => ({
handle: handle.value,
country: shopStore.buyerCountryCode,
language: shopStore.buyerLanguageCode,
}))
const { data: productData } = await useAsyncData(
`product-${handle.value}`,
() => shopify.product.get(productVars.value),
{ watch: [productVars] },
)
// Response data
const product = computed(() => productData.value)Ideal for working with actions in Pinia:
// Shopify
const shopify = useShopify()
// Cart store actions
actions: {
async createCart(input?: CartInput, optionalParams?: CartOptionalInput) {
try {
const response = await shopify.cart.create({
input: input,
...optionalParams,
})
if (response?.userErrors?.length) {
throw new Error(response?.userErrors[0]?.message)
}
this.cart = response?.cart
} catch (error) {
console.error('Cannot create cart:', error)
throw error
}
},
// More cart actions...
}A handy flattenConnection utility function is provided to make working with GraphQL connection objects much more simple. This utility extracts and flattens nested node arrays, making your node data easier to work with:
// Access product variant nodes
const variants = computed(() =>
flattenConnection(product.value?.variants) as ProductVariantFragment[]
)
// Use node data for something...
const currentVariant = computed(() =>
variants.find((variant) =>
variant.selectedOptions.every(({ name, value }) =>
isSizeOption(name) ? value === selectedSize.value : true,
),
),
)Contributions are always welcome! If you’d like to help improve this project, here’s how you can get involved:
- Post an issue: Use the Issues tab to report bugs or request new features.
- Start a discussion: Share ideas or ask for help in the Discussions tab.
- Submit a pull request: If you’d like to contribute, fork the repository, make your changes, and submit a pull request for review.
I actively monitor this repository and will do my best to respond quickly. Whether it’s fixing a small typo or adding a new feature, every contribution helps!