Customize the sitemap
Since version 3.4
Introduction
This guide provides step-by-step instructions on how to customize the sitemap for your Front-Commerce application. Customizing your sitemap can help improve SEO by ensuring search engines can easily crawl and index your site's content.
Static Pages Customization
getSitemapEntries
Function
To customize the sitemap for static pages, use the getSitemapEntries
function
in the SEO handle. This function allows you to add custom URLs, set the last
modification date, change frequency, and priority for each URL.
Example:
import { createHandle } from "@front-commerce/remix/handle";
export const handle = createHandle({
getSitemapEntries: ({ request, app }) => [
{
url: "/blog",
lastmod: new Date(),
changefreq: "daily",
priority: 0.5,
images: ["/blog-image.jpg"],
},
],
});
Ensure server-only logic in getSitemapEntries
is implemented using
vite-env-only
to prevent leaking into client-side code.
Dynamic Pages Customization
Dynamic pages can also be customized using getSitemapEntries
or a custom
sitemapFetcher
.
getSitemapEntries
Function
import { createHandle } from "@front-commerce/remix/handle";
import { serverOnly$ } from "vite-env-only";
export const handle = createHandle({
getSitemapEntries: serverOnly$(async ({ request, app }) => {
const posts = await fetch(
"https://jsonplaceholder.typicode.com/posts"
).then((res) => res.json());
return posts.map((post) => ({
url: `/todo/${post.id}`,
}));
}),
});
sitemapFetcher
Option
Refer to Registering Sitemap Fetcher for custom fetcher registration.
import { createHandle } from "@front-commerce/remix/handle";
export const handle = createHandle({
sitemapFetcher: "todoSitemapFetcher",
});
Creating and Registering Custom Fetchers
Before creating new fetchers let's dive into the SitemapService
service to get
a better understanding of how it works.
The SitemapService
is composed of two main parts:
- Composite: A list of fetchers that are executed in parallel to generate the sitemap.
- FetcherLeaf: A function that returns an array of sitemap entries.
Here is a diagram to help you visualize the Sitemap Service:
As you can see, each service is able to register it's own fetchers for a composition, this will help with customisation later on in the guide.
Register a composition
Compositions are generally registered by the extensions which are responsible
for adding the handle
in the routes.
In theme chocolatine, we have already have a few compositions (products
,
category
, cms
).
You can register your own composition in your extension definition:
import { createSitemapFetcher } from "@front-commerce/core";
export default defineExtension({
name: "acme",
meta: import.meta,
unstable_lifecycleHooks: {
onServerServicesInit: async (services, request, config) => {
services.Sitemap.registerComposition("acmeComposition");
},
},
});
Registering a Fetcher
First we will learn how to register a fetcher, the next section will cover how to create a fetcher.
Registering a fetcher is similar to registering a composition, you can do it in your extension definition.
If you register a fetcher to a non-existent composition, the composition will be added automatically.
import { defineExtension } from "@front-commerce/core";
export default defineExtension({
name: "acme",
meta: import.meta,
unstable_lifecycleHooks: {
onServerServicesInit: async (services, request, config) => {
services.Sitemap.registerFetcher("todoComposition"
"AcmeTodo",
() => import("./sitemap/todo.ts")
);
// We can also register other fetchers for the `todoComposition`, for example:
// services.Sitemap.registerFetcher("todoComposition"
// "ContentfulTodo",
// () => import("./sitemap/contentful/todo.ts")
// );
},
},
});
Creating a Fetcher
The fetcher is runtime logic, which will be resolved when a request to the
sitemap.xml
page is made.
Here is an example of a fetcher that fetches a list of todos from a remote API, and generates the sitemap entry for each todo.
import { createSitemapFetcher } from "@front-commerce/core";
export default createSitemapFetcher(async () => {
const posts = await fetch("https://jsonplaceholder.typicode.com/posts").then(
(res) => res.json()
);
return posts.map((post) => ({
route: `/todo/${post.id}`,
changefreq: "monthly",
lastmod: new Date(post.updatedAt),
priority: 0.5,
images: [post.image],
data: post, // we add the full post data which allows for custom filter logic
}));
});
Filtering sitemap entries from a fetcher
To filter specific entries from a fetcher, you can register a filter in your extension definition.
Filters are applied directly to the FetcherLeaf
, so this allows different
filters based on different FetcherLeaf
's in the same composition.
import { defineExtension } from "@front-commerce/core";
export default defineExtension({
name: "acme",
meta: import.meta,
unstable_lifecycleHooks: {
onServerServicesInit: async (services, request, config) => {
// We can add the data types through the generic <{ status: boolean }> typing
services.Sitemap.registerFetcherFilter<{ status: boolean }>(
"AcmeTodo",
// The entries returned are only those of the `AcmeTodo` fetcher leaf.
async (entries) => {
return entries.filter((entry) => entry.data?.status === "published");
}
);
},
},
});
Extending the type declarations
For TypeScript support, you'll need to extend the SitemapCompositionList
and
SitemapFetcherList
interfaces from @front-commerce/types
to include your
custom composites and fetchers.
First ensure you have the @front-commerce/types
package installed in your
project, as it provides the necessary types for the SitemapService
.
pnpm add -D @front-commerce/types
Then you can create a new types declaration file in your extension:
declare module "@front-commerce/types" {
export interface SitemapCompositionList {
todoComposition: "todoComposition";
}
export interface SitemapFetcherList {
AcmeTodo: "AcmeTodo";
}
}
These types will be merged with the existing types, so you can do this multiple times across multiple extensions.
The declaration file needs to be included in your tsconfig.json
before
typescript can pick it up.
{
"include": ["types/**/*"]
"include": ["extensions/**/types/**/*", "types/**/*"]
}
Opting Out of Sitemap Generation for static pages
Static pages are automatically included in the sitemap. To exclude a page from
the sitemap, return null
or an empty array from getSitemapEntries
.
import { createHandle } from "@front-commerce/remix/handle";
export const handle = createHandle({
getSitemapEntries: () => null,
});