How to Create Sitemap in Remix: Step-by-Step Guide
To create a sitemap in Remix, define a route that returns XML content in its
loader function with the Content-Type header set to application/xml. Generate the sitemap XML string dynamically and return it using Remix's Response object.Syntax
In Remix, a sitemap is created by defining a route with a loader function that returns an XML string wrapped in a Response object. The response must include the Content-Type: application/xml header to tell browsers and crawlers this is XML content.
The basic syntax includes:
loader(): async function to generate sitemap XMLResponse(): returns the XML string with headersContent-Typeheader: set toapplication/xml
javascript
export async function loader() { const sitemapXml = `<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>https://example.com/</loc> <changefreq>daily</changefreq> <priority>1.0</priority> </url> </urlset>`; return new Response(sitemapXml, { headers: { 'Content-Type': 'application/xml' } }); }
Example
This example shows a complete Remix route that generates a sitemap XML dynamically for two pages. It returns the XML with the correct header so search engines can read it.
typescript
import { LoaderFunction } from '@remix-run/node'; export const loader: LoaderFunction = async () => { const urls = [ { loc: 'https://example.com/', changefreq: 'daily', priority: 1.0 }, { loc: 'https://example.com/about', changefreq: 'monthly', priority: 0.7 } ]; const sitemapXml = `<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> ${urls .map( ({ loc, changefreq, priority }) => ` <url> <loc>${loc}</loc> <changefreq>${changefreq}</changefreq> <priority>${priority}</priority> </url>` ) .join('')} </urlset>`; return new Response(sitemapXml, { headers: { 'Content-Type': 'application/xml' } }); };
Output
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<changefreq>daily</changefreq>
<priority>1</priority>
</url>
<url>
<loc>https://example.com/about</loc>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
</urlset>
Common Pitfalls
- Missing Content-Type header: Without
application/xml, browsers and crawlers may not recognize the response as a sitemap. - Incorrect XML format: Sitemap XML must follow the sitemap protocol; invalid XML will be ignored by search engines.
- Not using a
Responseobject: Returning plain strings without headers will not set the correct content type. - Hardcoding URLs: Avoid hardcoding URLs; generate them dynamically if possible to keep sitemap updated.
javascript
/* Wrong way: Missing Content-Type header */ export async function loader() { const sitemapXml = `<?xml version="1.0" encoding="UTF-8"?><urlset></urlset>`; return sitemapXml; // This returns a string without headers } /* Right way: Return Response with headers */ export async function loader() { const sitemapXml = `<?xml version="1.0" encoding="UTF-8"?><urlset></urlset>`; return new Response(sitemapXml, { headers: { 'Content-Type': 'application/xml' } }); }
Quick Reference
- Use
loaderto generate sitemap XML dynamically. - Return a
ResponsewithContent-Type: application/xml. - Follow sitemap XML schema from sitemaps.org.
- Keep URLs updated to reflect your site structure.
Key Takeaways
Create a Remix route with a loader that returns sitemap XML wrapped in a Response object.
Always set the Content-Type header to application/xml for sitemap responses.
Generate sitemap XML dynamically to keep URLs current and valid.
Follow the sitemap XML protocol to ensure search engines can read your sitemap.
Avoid returning plain strings without headers to prevent content type issues.