Using Vuestorefront 2 SSR with Nuxt 3 to Improve Initial Load and SEO
Learn how Vuestorefront 2’s Nuxt 3 base enables server‑side rendering, see a worked example of adding a custom middleware endpoint for promotional banners, and understand the trade‑offs involved.
21 Jan 2026, 13:10 UTC

Problem: Slow first paint and poor SEO in a headless storefront
When you build a storefront with Vuestorefront 2 (VSF 2) using only client‑side rendering, the browser must download JavaScript, execute it, and then call the middleware API before any product data appears. This leads to a high Time‑to‑First‑Byte (TTFB), low First Contentful Paint (FCP), and search engines seeing an empty page.
Thesis: Nuxt 3’s built‑in server‑side rendering solves both issues
VSF 2 is constructed on Nuxt 3, which provides automatic SSR, code‑splitting, and isomorphic composables. By rendering pages on the server, the initial HTML already contains product titles, prices, and any data fetched via server‑side composables, improving TTFC, FCP, and SEO without extra boilerplate.
Worked example: Adding a custom middleware endpoint for promotional banners
Suppose you want to display a promotion banner that should be indexed by search engines. You can extend the VSF middleware with a new route and consume it through a server‑side composable.
- Clone the starter and install dependencies (run in your terminal, you need read/write access to the project folder):
git clone https://github.com/Vuestorefront/vue-storefront-2.git cd vue-storefront-2 npm install - Create the custom endpoint under
api/src/routes/custom-promotions.ts. The file must follow the VSF API shape: return an object with adataarray and proper error handling.import { defineEventHandler } from 'h3' export default defineEventHandler(async (event) => { // In a real implementation you would fetch from a CMS or database const promo = { id: 'spring-sale', title: 'Spring Sale – 20% off', bannerUrl: '/images/spring-banner.jpg' } return { data: [promo] } }) - Expose the endpoint in the middleware by adding it to
api/src/routes/index.ts:import customPromotions from './custom-promotions' export default defineNuxtRouteMiddleware({ '/api/custom-promotions': customPromotions }) - Create a server‑side composable in
composables/useCustomPromotions.ts:import { useAsyncData } from '#app' export const useCustomPromotions = () => { return useAsyncData('custom-promotions', () => $fetch('/api/custom-promotions').then((res) => res.data) ) } - Use the composable in a page (e.g.,
pages/index.vue) to render the banner server‑side:{{ promo.data[0].title }}
- Start the development server and verify the result:
npm run devOpen
http://localhost:3000, right‑click → “View page source”. You should see the banner title and image URL directly in the HTML, not injected by client‑side JavaScript. - Check SEO impact with Lighthouse (Chrome DevTools → Lighthouse → “SEO” and “Performance” categories). Compare the SSR build to a client‑only SPA build (you can create the latter by setting
ssr: falseinnuxt.config.tstemporarily). You should observe a higher SEO score and improved FCP/TTFB numbers.
Trade‑offs and limitations
- Increased server load: Every request now runs Node.js to render HTML, which can raise cost and latency, especially in serverless platforms with cold starts.
- Node.js version requirement: VSF 2 requires Node ≥18; mismatched versions cause runtime errors.
- Middleware contract: Custom endpoints must return the expected shape (
{ data: [] }) and handle errors; otherwise composables may throw or render empty UI. - Cache‑ability: Fully dynamic SSR pages are less cache‑friendly; consider using
useAsyncDatawithdefault: trueor edge caching for static‑like data.
Practical way to verify the implementation
- View page source after a refresh – confirm server‑rendered promo content appears.
- Run
npm run build && npm run startto test production‑like SSR behavior. - Use Lighthouse to record performance metrics before and after enabling SSR.
- Monitor server CPU/usage (e.g., via
pm2or container metrics) to gauge added load.
Actionable closing
If your Vuestorefront 2 storefront suffers from slow initial load or poor SEO, enable Nuxt 3 SSR (the default) and add any needed data via server‑side composables or custom middleware endpoints. Verify the HTML output, run Lighthouse, and watch server metrics to ensure the trade‑off aligns with your traffic and infrastructure constraints.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.