A headless architecture empowers eCommerce businesses to deliver lightning‑fast, highly interactive storefronts without sacrificing search engine visibility. By decoupling content and presentation layers, you gain full control over crawlable sitemaps, semantic URLs, and metadata management—all critical for ranking in today’s AI‑driven search landscape.
So, what (and how) can you control via a framework or platform of your choice in a headless setup for SEO? Fortunately, as a headless commerce platform, Crystallize offers a rather unique perspective.
(Everything else ecommerce SEO can be found in the linked article)
Implementing Sitemaps & robots.txt & LLM.txt in Your Headless Frontend
Next.js
-
robots.txt
: Simply create a robots.txt file in your project’s/public directory
. -
sitemap.xml
: Either hand‑craft a static sitemap.xml under/public
or generate it at runtime viagetServerSideProps
, returning XML with your product and category URL.
Astro
- Use the astro-sitemap integration to crawl static routes and output
sitemap.xml
. - Leverage
astro-robots-txt
to auto‑generaterobots.txt
based on your published page.
SvelteKit
- Define a
+server.js
endpoint that returns XML for yoursitemap.xml
, mapping over your route manifest. - Create a separate
+server.js
or static asset forrobots.txt
, listing disallowed paths (e.g.,/admin/*
,/search/*
).
While robots.txt
guides crawlers on URL access, llms.txt
, an experimental best practice, helps LLMs prioritize, fetch, and order content. It should not be considered a guaranteed method for driving traffic or indexing. Implementation-wise, you can upload it to your project’s public directory.
Alternatively, you can rely on a platform you are using (like Crystallize) to define LLM text per page that would be included in the file at build time. Basically, you’d model out the fields you need from scratch.
Custom 4XX Error Pages
Delivering branded, helpful error pages improves UX and ensures crawlers don’t get stuck on dead ends.
-
Next.js: Drop a
pages/404.js
file—Next will statically generate it at build time. -
Astro: Add
404.astro
(or 404.md) in/src/pages;
Astro will serve it for any missing route. -
SvelteKit: Use a Svelte component at
+error.svelte
to customize both 404 and other 4XX responses.
For complete control, you can also configure your hosting platform’s error‐page behavior (e.g., Netlify’s _redirects
file or a Vercel error
edge function).
Managing 301 Redirects
Permanent redirects preserve link equity when URLs change. In headless setups, you can choose framework‑level or hosting‑level redirects.
Next.js: Define a redirects
array in next.config.js
:
module.exports = {
async redirects() {
return [
{ source: '/old-path', destination: '/new-path', permanent: true },
];
},
};
Astro: In astro.config.mjs
, add:
export default {
redirects: [
{ from: '/old-url', to: '/new-url', status: 301 },
],
};
SvelteKit: Use the redirect helper in your +server.js
:
import { redirect } from '@sveltejs/kit';
export function GET() {
throw redirect(301, '/new-location');
}
Hosting‑Level (Netlify/Vercel/Cloudflare):
Netlify: _redirects
file in publish folder:
/old-path /new-path 301
Vercel: vercel.json
with a redirects array.
Cloudflare Pages: Plain‑text _redirects
in your output directory.
Rendering Canonicals in a Decoupled Frontend
To prevent duplicate‑content issues, emit a self‑referencing or preferred canonical URL tag in your headless pages.
Next.js: Use <Head>
(or next-seo) in your page component:
import Head from 'next/head';
// ...
<Head>
<link rel="canonical" href={`https://example.com${router.asPath}`} />
</Head>
Astro: Build the canonical using Astro.url
in your
---
const canonical = Astro.url.pathname;
<link rel="canonical" href={`https://example.com${canonical}`} />
SvelteKit: In your +layout.svelte
or page component:
<svelte:head>
<link rel="canonical" href="https://example.com{$page.url.pathname}" />
</svelte:head>
Embedding these tags at build or render time ensures crawlers always see the correct URL.
3rd Party Scripts
Astro components support interactive elements through standard HTML <script>
tags. For Next.js, next/script
enhances the HTML <script>
element to optimize the fetching and execution of additional scripts.
Structured Data
In a Next.js project, you can manage structured data (JSON‑LD) just as you would any other page head content—by injecting a <script type="application/ld+json">
block via the built‑in
Here are a couple of ways you can do it:
Assemble your schema object. In your page’s data‑fetching function (e.g. getStaticProps
or getServerSideProps
), build the JSON‑LD payload.
// pages/products/[slug].js
export async function getStaticProps({ params }) {
const product = await fetchProduct(params.slug);
const schema = {
"@context": "https://schema.org/",
"@type": "Product",
name: product.name,
image: product.images,
description: product.description,
sku: product.sku,
brand: { "@type": "Brand", name: product.brand },
offers: {
"@type": "Offer",
priceCurrency: "USD",
price: product.price,
availability: "https://schema.org/InStock",
},
};
return { props: { product, schema } };
}
Inject via <Head>
. In your page component, serialize and insert the JSON‑LD inside the <Head>
tag so it’s rendered on the client and server.
import Head from 'next/head';
export default function ProductPage({ product, schema }) {
return (
<>
<Head>
<title>{product.name} -- My Shop</title>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
</Head>
{/* ...rest of your page... */}
</>
);
}
Use a helper library (optional). For larger sites, next-seo simplifies SEO—including structured data—by letting you define your schema in a centralized config.
After deployment, test each page in Google’s Rich Results Test or Schema Validator to ensure your JSON‑LD is correct and being picked up by crawlers.
If you have repeated schema across many pages (e.g., sitewide Organization or Breadcrumb data), consider adding a custom _document.js
or a top‑level layout component that injects those elements globally.
As for Astro, you treat structured data much the same way you would in any static‐site setup, including the above one with Next.js.
Your CMS or e-commerce platform defines structured data. Crystallize, for example, utilizes content modeling to describe all the necessary product information for your product page (including structured data, LLM summary, etc.). Your chosen frontend can then request and display this information via the API, including in Schema.org format.
Images Handling
Optimizing images can be achieved through various methods. Many frontend frameworks offer built-in solutions:
- Next.js: The
<Image/> component
handles rendering and optimizes images for different viewports by incorporating lazy loading, responsiveness, resizing, and optimized file size and format by default. - Svelte: Vite's built-in image handling is often the most effective approach.
- Astro: The
<Image /> Astro component
optimizes both local and configured remote images. Alternatively, consider using a third-party service, such as Cloudflare Image Optimization.
Finally, some CMS and e-commerce platforms like Crystallize provide image optimization as part of their service. For instance, Crystallize automatically compresses and converts uploaded images to AVIF and WebP formats, resizing them into multiple resolutions (e.g., 100px, 300px, up to the original image's maximum width). The @crystallize/react-image
npm package then assists with rendering responsive and fast images.
Conclusion
By surfacing robots.txt
, sitemap.xml
, custom error pages, 301 redirects, and canonical tags directly in your headless frontend or hosting layer, you maintain tight control over SEO without sacrificing the flexibility of modern frameworks.
Continue with the original and complete version of the guide at the Crystallize blog 👉eCommerce SEO Guide: How To Drive Organic Traffic In 2025?
Top comments (0)