@frontic/nuxt
v1.3.0
Published
Nuxt module for Frontic - data fetching composables with smart caching, page routing, and context management
Downloads
1,337
Readme
@frontic/nuxt
Nuxt module for building with the Frontic API and Frontic UI components. Provides auto-imported composables for data fetching with smart caching, page routing, and context management.
Features
- Smart caching via Pinia Colada - stale-while-revalidate, request deduplication, SSR hydration
- Type-safe composables - Full TypeScript support with generated types from your Frontic schema
- Search & filtering - Ready-to-use search state management with filters, sorting, pagination, and infinite scroll
- Page routing - Dynamic page resolution with automatic redirects and 404 handling
- Context management - Locale and region switching with cookie persistence
- Infinite scroll - Bidirectional infinite loading with deep-link support via Pinia Colada's
useInfiniteQuery - Analytics hooks - Typed
frontic:listing:resolved,frontic:search:committedandfrontic:page:resolvedevents for any analytics vendor - Built-in proxy - CORS-free API requests through your Nuxt server
Installation
npx nuxi@latest module add @frontic/nuxtQuick Start
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@frontic/nuxt'],
frontic: {
contextDomain: 'demo-shop.com',
},
})Generate the Frontic client:
npx @frontic/cli generateComposables
useFronticPage
Dynamic page routing with automatic redirects and 404 handling:
const { page, data, type, block, alternates } = useFronticPage()useFronticSearch
Full-featured search with filtering, sorting, and pagination:
const { result, state, searchTerm, filterResult, sortResult, loadNext } = useFronticSearch(
'ProductSearch',
{},
{
filter: {
select: ['properties.color', 'properties.size'],
label: { 'properties.color': 'Color' },
},
sorting: {
label: { 'name:asc': 'Name A-Z', 'price.amount:asc': 'Price: Low to High' },
},
}
)useFronticListing
Simple listing access:
// Second argument is the listing's parameters; query options go in the third
const { listing, status, error } = useFronticListing('CategoryProducts', { categoryKey: 'shoes' }, { query: { limit: 10 } })useFronticBlock
Fetch single blocks (products, categories):
const { block, status, error } = useFronticBlock('ProductCard', 'product-handle')Every data composable returns status and error alongside its data, plus refresh and refetch.
useFronticTree
Hierarchical menu trees:
const { items, status } = useFronticTree('CategoryNavigation', { depth: 2 })useFronticContext
Manage locale and region:
const { contexts, current, update } = useFronticContext()
await update({ region: 'de', locale: 'de-DE' })The selection is stored against a context token, persisted in the fs-context
cookie. Every composable resolves that token and sends it — you do not pass it
per call — and it is part of every cache key, so a switch re-keys each active
query and the new context's content is fetched. contextKey overrides it for one
call, contextKey: false opts a call out so it resolves through the domain
alone (useful behind a shared cache), and disableContext: true hands token
management to you entirely.
Server routes
useFronticClient is a Nuxt app composable and does not run in Nitro. A server/
route uses createFronticServerClient(event), which resolves the context domain
from config, the visitor's token from the request's context cookie, and the
request URL from the event — and reports failures on the Nitro
frontic:fetch:error hook:
// server/api/stock.get.ts
import { createFronticServerClient } from '@frontic/nuxt/server'
export default defineEventHandler(async (event) => {
const client = createFronticServerClient(event)
return await client.listing('ProductSearch', {}, { query: { limit: 5 } })
})The API secret needs no wiring — the module's Nitro plugin attaches it to every
Frontic request the server makes. contextDomain: true (read the active i18n
locale) cannot resolve here, since there is no i18n instance in Nitro; pass the
domain per call in that setup.
The module's disableContext is what decides whether the cookie is read here —
a per-instance useFronticContext({ disableContext }) override lives inside the
Nuxt app, and a server route cannot see it. Pass contextKey explicitly if your
app overrides the module setting and also fetches server-side.
Configuration
export default defineNuxtConfig({
frontic: {
// Page routing
contextDomain: 'demo-shop.com',
redirectOn301: true,
throwOn404: true,
// API proxy
proxy: true, // or '/api/custom-path'
fetchApiSecret: process.env.FRONTIC_FETCH_SECRET, // only if the project's Fetch API is protected
// Sitemap
sitemap: true,
// Context cookies
contextCookieName: 'fs-context',
contextCookieMaxAge: 31536000,
// Composables
composables: true, // or ['page', 'search', 'listing', 'block', 'tree', 'context', 'client']
// UI components (optional)
componentsPrefix: '',
componentDir: '@/components/ui',
},
})Sitemap
With sitemap: true, requests to /sitemap.xml (and /sitemapN.xml for split sitemaps) on any locale prefix serve the Frontic sitemap for the matching domain — generated from the Pages marked "Visible in Sitemap". Responses carry the upstream ETag and are cached with stale-while-revalidate. When your config declares domains (sitemap: { domains: [...] }, a contextDomain string, or contextDomain: true with i18n locales carrying contextDomain properties), only those domains are served — other hosts get a 404. With no declared domains the route serves any well-formed forwarded host and the module warns at build time. When @nuxtjs/robots is installed, the sitemap is added to robots.txt automatically. Not compatible with @nuxtjs/sitemap yet — with both installed the option is ignored with a warning.
Multi-locale domains
Frontic resolves scope, region and locale from the domain each request carries. Set contextDomain: true to take it from the locale @nuxtjs/i18n currently has active, instead of hardcoding one:
export default defineNuxtConfig({
modules: ['@frontic/nuxt', '@nuxtjs/i18n'],
frontic: {
contextDomain: true,
},
i18n: {
defaultLocale: 'en',
locales: [
{ code: 'en', contextDomain: 'demo-shop.com' },
{ code: 'de', contextDomain: 'demo-shop.com/de' },
],
},
})contextDomain on the locale object is a property you add — the module reads it through i18n's localeProperties, and each value must match a domain configured in your Frontic project. The resolved domain is part of every cache key, so locales never share a cached response.
Per call, any composable accepts contextDomain to override it, and useFronticClient({ contextDomain: false }) sends none at all.
Analytics
Listing, search and page events are announced as typed Nuxt hooks, so any vendor can be wired up in one plugin:
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('frontic:listing:resolved', (payload) => {
if (payload.trigger === 'initial') {
track('view_item_list', { list: payload.listing, count: payload.resultCount })
}
})
})Hooks are client-only and deduplicated per route entry. Payload types are importable from @frontic/nuxt/types when you factor handlers out of the plugin.
TypeScript
Composable typing comes from your generated client (npx @frontic/cli generate): block, listing and tree names, listing parameters, filter and sort fields, and response types all resolve to your project's schema, and invalid names fail the typecheck. Helper types are available from @frontic/nuxt/types:
import type { ResponseOf, ListingQuery, SortString, FronticClient } from '@frontic/nuxt/types'Documentation
Full documentation at docs.frontic.com
License
MIT
