Guide Remix SEO
Comment Remix's meta(), loader(), headers(), et liens() route exports contrôler SEO — plus ce que modifié as Remix v2 became React Router, maintenant on v8.
«> TL;DR — Remix is a React framework that renders your pages on the server
by default — so the content is already in the HTML before Google ever loads it. That goes a long way toward solving the biggest JavaScript SEO problem, though it doesn’t guarantee indexing or rankings by itself. You still have to set your titles, descriptions, and canonical tags yourself, using Remix’s per-route
meta()function. SSR is the foundation, not the whole house. Note: Remix v2 merged into React Router (now v8) — Remix 3 (beta) is a separate, newer product; this page covers the Remix v2 / React Router route API most sites run today. » (Traduction) (Résumé en français de la section trois, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
Ce que Remix is
Remix routes peut render on le serveur and provide document metadata via route APIs. Preuve à l’appui de cette affirmation Primary standard or official documentation supporting the adjacent article claim. Portée : Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Niveau de confiance : élevé · Vérifié : Remix: Route meta Server-rendered HTML improves initial content delivery but ne fait pas guarantee Search indexation. Preuve à l’appui de cette affirmation Primary standard or official documentation supporting the adjacent article claim. Portée : Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Niveau de confiance : élevé · Vérifié : Google: JavaScript SEO basics
Remix est a meta-framework construit on React. Plain React construit votre entier page dans le navigateur avec JavaScript — Google obtient a nearly vide HTML fichier et a à exécuter votre code à voir n’importe quoi. Remix flips que: il exécute votre React components on le serveur et sends back finished HTML avec le texte, liens, et meta balises déjà dans il. Serveur rendu est le framework par défaut — a project peut aussi être configuré à statically prerender routes or exécuter dans SPA mode, donc confirmer qui mode a donné site en réalité utilise plutôt que assuming depuis le framework nom alone.
Pour le SEO, c’est a huge head commencer on le par défaut SSR chemin. il y a aucun waiting pour JavaScript, aucun “render queue,” et robots d’exploration que ne pas exécuter JavaScript à tout (nombreux AI bots, social preview bots) encore obtenir votre complet contenu.
Un naming remarque que trips personnes up, et c’est plus layered que il utilisé à être:
dans late 2024 le Remix team merged Remix v2’s application APIs dans React
Router v7 — même route exports (meta, loader, headers, links,
ErrorBoundary), renamed package. React Router a since déplacé à v8 (June
2026), avec v7 encore receiving security mises à jour. Séparément, le Remix nom maintenant
aussi refers à Remix 3, a newer beta complet-stack framework le team décrit
as “a different direction” depuis React Router — a distinct produit, pas simplement a
renamed un. Tout on ce page documents le Remix v2 / React Router v7-v8
route API que la plupart production Remix sites exécuter aujourd’hui; il ne fait pas cover Remix 3.
How vous ajouter SEO tags in Remix
Remix donne chaque route (chaque page) special functions vous export depuis le fichier:
meta()— sets votre<title>, meta description, Ouvrir Graph balises, et même données structurées. Ce est le principal SEO outil.loader()— récupère données on le serveur (comme a blog post depuis votre database) avant le HTML est sent. Votremeta()peut utiliser que données, donc titles peut être dynamic.links()— ajoute<link>balises comme favicons et stylesheets.headers()— sets HTTP réponse headers (mise en cache, robots règles).
Ici’s le simplest version de a titre et description:
export const meta = () => {
return [
{ title: "About Us | My Site" },
{ name: "description", content: "Learn about our team and mission." },
];
};Que renders straight into le serveur HTML — exactly où Google veut it.
The un mistake to éviter
Le la plupart courant Remix SEO bug: child pages quietly lose le meta balises définir par
parent layouts. Remix seulement garde le deepest route’s meta() et throws away
le rest unless vous tell il pas à. Si votre homepage layout sets a site-wide
description et votre À propos de page exports son propre meta() sans merging, le
description disappears on que page. Le corriger est dans le Avancé tab.
The simple takeaway
«- Remix is a safe SEO choice because it’s server-rendered by default.
» (Traduction) (Résumé en français de la section dix-huit, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
«- You still have to write your titles, descriptions, and canonicals with meta().
» (Traduction) (Résumé en français de la section dix-huit, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Watch out for child pages dropping parent meta tags.
» (Traduction) (Résumé en français de la section dix-huit, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.)
Vouloir le réel version — loader et réel 404s, headers() pour X-Robots-Tag,
balise canonicals deux façons, et le React Router v7 migration? Switch à le
Avancé tab. Pour le framework-agnostic background, voir
JavaScript SEO et le
JavaScript frameworks hub.
TL;DR — Remix est SSR-par-default, donc contenu ships dans le initial HTML avec aucun rendre-queue dependency — le architecture supprime la plupart JS-SEO risque quand SSR est qu’est-ce que en réalité deployed, though il ne fait pas par itself guarantee indexation, classement, or Core Web Vitals résultats. Le fonctionner est dans le per-route exports:
meta()(titles, descriptions, OG, JSON-LD, et dynamic canonicals viatagName: "link"),loader()(serveur données + réel HTTP 404s/redirections),headers()(Cache-Control,X-Robots-Tag), etlinks()(static canonicals, preloads). Le signature gotcha: nested routes drop parent meta unless vous merge il via lematchesargument. Naming, dated: Remix v2’s APIs merged dans React Router v7 dans 2024; React Router est maintenant on v8 (v7 encore security-pris en charge); Remix 3 (beta) est a séparé, newer complet-stack framework, pas simplement React Router renamed. Ce page covers le Remix v2 / React Router v7-v8 route API.
SSR by par défaut — pourquoi Remix starts ahead
Remix’s serveur rendu est a framework capability whose deployment behavior dépend on le adapter et route code. Preuve à l’appui de cette affirmation Primary standard or official documentation supporting the adjacent article claim. Portée : Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Niveau de confiance : élevé · Vérifié : Remix: Route meta Valider le delivered HTML et resources plutôt que assuming a framework par défaut ensures SEO résultats. Preuve à l’appui de cette affirmation Primary standard or official documentation supporting the adjacent article claim. Portée : Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Niveau de confiance : élevé · Vérifié : Google: JavaScript SEO basics
Scope remarque: le walkthrough ci-dessous documents le meta() / loader() /
headers() / links() / ErrorBoundary route module API as il exists dans Remix
v2 et React Router v7-v8 framework mode — ce que le grand majority de production
Remix/React Router sites exécuter. Remix 3 (beta) est a séparé, ground-up rewrite
avec différent APIs et n’est pas covered ici; vérifier qui un vous êtes en réalité on
avant copying n’importe quoi ci-dessous.
Bare React (CRA, Vite + React) ships an vide <div id="root"></div> et construit
lune page dans le navigateur. Google peut rendre que, mais vous avez signed up pour le
rendre queue, statelessness, et DOM-parity problèmes covered dans
JavaScript SEO. Remix ne fait pas ont que par défaut:
il executes votre nested route tree (Root → Layout → Route) on le serveur et, par
par défaut, sends contenu-complet HTML on chaque document requête. Framework mode
peut aussi être configuré à statically prerender routes à construire temps or exécuter as a
client-rendu SPA — donc pour quelconque donné deployment, confirmer le réel rendu
mode (via curl/View Source, ci-dessous) plutôt que assuming SSR depuis le framework
nom.
«Google’s own guidance lines up with this. From the JavaScript SEO basics doc:
“Server-side or pre-rendering is still a great idea because it makes your website
faster for users and crawlers, and not all bots can run JavaScript.” That last
clause is the whole argument for Remix — Bingbot renders JS slowly and incompletely,
and many AI crawlers and social preview bots (Twitterbot, facebookexternalhit)
don’t render at all. Remix’s SSR output serves all of them correctly without special
handling.
» (Traduction) (Résumé en français de la section vingt-sept, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
Après le initial charger, Remix utilise client-side routing pour subsequent navigations
(comme chaque React framework). c’est fine: Google discovers liens depuis le SSR’d
HTML et explore chaque URL as son propre serveur requête — chaque returning complet HTML. Il
ne fait pas rely on watching client-side route transitions. Et parce que Remix’s <Link>
renders a réel <a href>, liens sont crawlable par construction — Google
“peut seulement découvrir votre liens si ils sont <a> HTML elements avec an href
attribut.”
Le meta() export: titles, descriptions, OG, JSON-LD
Chaque route peut export a meta function returning an array de descriptor objects.
Parce que il peut lire loader données, metadata est dynamic et définir dans le initial HTML
<head> — aucun JS-injection delay:
// app/routes/blog.$slug.tsx
import type { MetaFunction } from "@remix-run/node";
export const meta: MetaFunction<typeof loader> = ({ data }) => {
if (!data) return [{ title: "Post Not Found" }];
return [
{ title: `${data.post.title} | My Blog` },
{ name: "description", content: data.post.excerpt },
{ property: "og:title", content: data.post.title },
{ property: "og:image", content: data.post.ogImage },
{ property: "og:type", content: "article" },
{
"script:ld+json": {
"@context": "https://schema.org",
"@type": "Article",
headline: data.post.title,
datePublished: data.post.publishedAt,
author: { "@type": "Person", name: data.post.author },
},
},
];
};«The { "script:ld+json": {...} } descriptor renders a proper
<script type="application/ld+json"> tag in the SSR’d <head> — visible to Google’s
parser without executing any JavaScript. Martin Splitt’s preference applies:
“We support JSON-LD in dynamically rendered content, but it’s generally better to
have it in the initial HTML.” In Remix, the initial HTML is the default place it
lands.
» (Traduction) (Résumé en français de la section trente-deux, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
«## Nested routes and meta inheritance — the common trap » (Traduction) (Résumé en français de la section trente-trois, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
«This is the Remix SEO mistake. Remix takes the last matching route with a meta
export and uses that — parent meta is dropped. A child that exports its own
meta() without merging silently loses every tag the root set (site-wide
description, root OG tags, etc.):
» (Traduction) (Résumé en français de la section trente-quatre, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
// ❌ Root's description is now gone on this route
export const meta: MetaFunction = () => {
return [{ title: "About Us" }];
};«The fix is the matches argument — flatten and spread parent meta:
» (Traduction) (Résumé en français de la section trente-six, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
// ✅ Keep parent meta, append your own
export const meta: MetaFunction = ({ matches }) => {
const parentMeta = matches.flatMap((match) => match.meta ?? []);
return [...parentMeta, { title: "About Us" }];
};
// ✅ Keep parent meta but override only the title
export const meta: MetaFunction = ({ matches }) => {
const parentMeta = matches
.flatMap((match) => match.meta ?? [])
.filter((meta) => !("title" in meta));
return [...parentMeta, { title: "About Us" }];
};«If you set global tags, put truly universal ones (charset, viewport) directly in
root.tsx’s JSX where merging never strips them, and reserve meta() for
page-level signals you actually want to override per route.
» (Traduction) (Résumé en français de la section trente-huit, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
«## The loader() function: real 404s, real redirects, dynamic meta
» (Traduction) (Résumé en français de la section trente-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
Loaders exécuter serveur-side seulement — DB requêtes, API calls, et secrets jamais reach Googlebot. Le SEO payoff est correct Code d’état HTTPs:
// app/routes/products.$id.tsx
import { json, redirect } from "@remix-run/node";
import type { LoaderFunctionArgs } from "@remix-run/node";
export async function loader({ params }: LoaderFunctionArgs) {
const product = await db.products.findById(params.id);
if (!product) {
throw new Response("Product not found", { status: 404 });
// ✅ real HTTP 404 — not a soft 404
}
if (product.movedTo) {
throw redirect(`/products/${product.movedTo}/`, 301); // ✅ real 301
}
return json(product);
} «This is the antidote to the soft-404 problem SPAs create — a 200 OK page that
just says “not found.” As John Mueller put it: “If a page returns a 200 but there’s
no content — that’s a soft 404. That’s problematic because we don’t know to treat it
as a 404. From a crawling standpoint, we’ll just keep trying to crawl it.” Remix’s
throw new Response(..., { status: 404 }) propagates a genuine 404, which lines up
with Google’s instruction to “use a meaningful status code, like a 404 for a page
that could not be found.”
» (Traduction) (Résumé en français de la section quarante-deux, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
Whatever a loader renvoie est exposed à le client même si le component ne fait pas rendre il — treat loaders comme public API endpoints et ne pas retourner secrets.
Ce status propagation est ce que se produit on a direct document requête (le cas a robot d’exploration rend). Client-side navigations après hydration sont a séparé code chemin que peut behave differently, et some hosting adapters rewrite or intercept thrown réponses — vérifier les deux le direct-requête et post-hydration behavior on votre réel deployment plutôt que assuming il depuis le loader code alone.
Le headers() function: Cache-Contrôle et X-Robots-Balise
Per-route HTTP headers — le lever pour CDN mise en cache (faster TTFB → meilleur Core Web Vitals) et pour robots directives que fonctionner même on robots d’exploration que ne pas lire le HTML corps:
import type { HeadersFunction } from "@remix-run/node";
// CDN-friendly caching
export const headers: HeadersFunction = () => ({
"Cache-Control": "max-age=300, s-maxage=3600, stale-while-revalidate=86400",
});
// HTTP-level noindex — effective for Bingbot and non-rendering crawlers
export const headers: HeadersFunction = () => ({
"X-Robots-Tag": "noindex, nofollow",
});«X-Robots-Tag: noindex at the header level reaches crawlers that never parse the
body. By default only the deepest route’s headers() runs in a nested tree, so
the simplest pattern is to define headers on leaf routes only and avoid merge
complexity.
» (Traduction) (Résumé en français de la section quarante-huit, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
A meta-noindex caveat worth knowing regardless de framework, depuis Martin Splitt:
“Le noindex balise peut causer Google à skip rendu entirely. Donc si vous êtes trying
à noindex via JavaScript, vous peut être creating a situation où Google jamais même
exécute le JavaScript à voir le noindex.” Remix sidesteps ce parce que le directive
lands dans le SSR’d <head> (or an HTTP header), pas dans client-seulement JavaScript.
The links() export and balise canonicals — two façons
links() injects <link> elements (favicons, stylesheet preloads, static
canonicals). But it has aucun accès to loader données — it’s static per route module:
export const links: LinksFunction = () => [
{ rel: "canonical", href: "https://example.com/canonical-url/" },
];Pour dynamic canonicals — paginated, filtered, or parameterized URLs — utiliser meta()
avec tagName: "link", qui fait voir data, params, et location:
export const meta: MetaFunction<typeof loader> = ({ data }) => [
{
tagName: "link",
rel: "canonical",
href: `https://example.com/products/${data.product.slug}/`,
},
];Le règle: static canonical → links(); dynamic canonical → meta() avec
tagName: "link". Ce matches Martin Splitt’s advice à définir canonicals dans HTML
plutôt que JavaScript — “définir votre canonical dans le HTML, pas avec JavaScript… c’est
simplement plus fragile.” Les deux Remix approaches rendre dans le serveur HTML. (Voir le
balise canonical deep dive.)
Erreur boundaries et 404/5xx pages
Quand a loader, action, or component throws, le route’s ErrorBoundary renders — dans
placer, à l’intérieur le surviving layout (nav, footer stay). Paired avec a thrown 404
Réponse, vous obtenir a réel 404 status et a usable page:
import { isRouteErrorResponse, useRouteError } from "@remix-run/react";
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error) && error.status === 404) {
return <div><h1>404 — Page Not Found</h1></div>;
}
return <div>Something went wrong.</div>;
}A root-level ErrorBoundary dans app/root.tsx catches n’importe quoi route boundaries
ne pas. Le myth que “throwing a 404 breaks the layout” est faux — le boundary
renders dans le route hierarchy.
Sitemaps et robots.txt as routes
Remix a aucun built-dans sitemap generator; vous construire les as resource routes. Le
bracket notation escapes le dot donc l’URL est literally /sitemap.xml:
// app/routes/sitemap[.xml].tsx
export async function loader() {
const posts = await db.posts.findMany({ where: { published: true } });
const body = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${posts.map((p) => ` <url><loc>https://example.com/blog/${p.slug}/</loc><lastmod>${p.updatedAt.toISOString()}</lastmod></url>`).join("\n")}
</urlset>`;
return new Response(body, {
headers: { "Content-Type": "application/xml", "Cache-Control": "public, max-age=3600" },
});
}robots.txt follows the même app/routes/robots[.txt].tsx pattern, returning
text/plain.
Remix, React Router, et le 2026 produit split — a dated timeline
Ce a gotten plus layered since le original 2024 merge, donc treat il as history plus actuel state plutôt que a unique fact:
«- Dec 2024 — Remix v2 merges into React Router v7. @remix-run/react and
@remix-run/node consolidate into react-router; the @remix-run/* packages
still work as a bridge. What’s SEO-relevant from this move:
routes.ts— a routing config file; routes can be defined programmatically for more flexible URL structures.- Static pre-rendering — individual routes can opt into build-time HTML generation (SSG-style), or SPA mode — configuration choices, not automatic.
- End-to-end type safety —
loaderreturn types flow intometa(), reducing bugs in dynamic meta generation. - Vite — the default bundler since Remix v2; route-level code splitting keeps client bundles lean (good for TBT/LCP).
- What didn’t change: the
meta()/loader()/headers()/links()/ErrorBoundarypattern and the<Meta />/<Links />/<Scripts />components inroot.tsx. Existing Remix v2 SEO code carries over with minimal changes. » (Traduction) (Résumé en français de la section soixante-six, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.) «- June 2026 — React Router v8 ships. React Router v7 remains security-supported; Remix v2 and React Router v6 are now end-of-life. The route module SEO API this page documents (meta,loader,headers,links,ErrorBoundary) is the same shape across v7 and v8 — check your installed version’s changelog for any descriptor-level changes before copying code verbatim. » (Traduction) (Résumé en français de la section soixante-six, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.) «- Also as of 2026 — Remix is no longer just “the old name for React Router.” The Remix team describes React Router as their React meta-framework, and Remix (now in a “Remix 3” beta) as a separate, newer full-stack framework — “one team, two projects.” If you’re auditing or building on@remix-run/*/react-routerroute exports, this article’s API guidance applies directly. If you’re on Remix 3, verify its documentation independently — the meta/loader/headers/links pattern described here is not confirmed to carry over unchanged. » (Traduction) (Résumé en français de la section soixante-six, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.)
Bottom line pour la plupart sites dans production aujourd’hui: “Remix SEO” et “React Router SEO” sont encore le même route API et le même practical topic. Simplement ne pas assume que aussi covers Remix 3.
Vous encore don’t besoin dynamic rendering
«Because Remix serves HTML on the server, you never need Rendertron or Prerender.io. Google deprecated dynamic rendering anyway: “Dynamic rendering was a workaround and not a long-term solution… it creates additional complexities and resource requirements.” SSR is the recommended path, and Remix gives it to you by default. » (Traduction) (Résumé en français de la section soixante-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
Auditing a Remix site
- Vérifier le initial HTML avec
curlor View Source — pas DevTools Elements (qui montre le post-hydration DOM). - Vérifier réel Code d’état HTTPs on manquant pages (404, pas 200) et redirections (301/302).
- Tester avec GSC Inspection d’URL — Remix’s servi HTML devrait match le rendu view.
- Regarder pour nested-meta merge bugs — confirmer child pages encore carry le site-wide description et OG balises.
- Valider JSON-LD avec le Résultats enrichis Tester, et exécuter a explorer (Ahrefs Site Audit, Screaming Frog) à catch bloqué assets et depth problèmes.
None de le ci-dessus est a guarantee de indexation, rankings, or Core Web Vitals — SSR, correct code d’états, et clean metadata supprimer architecture-level risque; le rest encore dépend on qualité du contenu, le deployment adapter, et comment le page performs contre tout sinon dans le SERP.
Pour le base library underneath Remix, voir React SEO; pour le framework-agnostic rendu règles, voir JavaScript SEO et le JavaScript frameworks hub.
AI summary
A condensed prendre on the Avancé version:
«- Remix is SSR-by-default — it renders the nested route tree on the server and,
by default, ships content-complete HTML on every document request (framework mode
can also be set to static prerender or SPA, so verify the actual deployed mode).
No render queue, no empty root <div>, and non-rendering crawlers (Bingbot, AI
bots, social bots) get full content — though none of this guarantees indexing or
rankings by itself.
» (Traduction) (Résumé en français de la section soixante-dix-huit, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
«- meta() is the primary SEO tool — titles, descriptions, OG, JSON-LD
("script:ld+json"), and dynamic canonicals via tagName: "link". It can read
loader data, so metadata is content-specific and in the initial HTML.
» (Traduction) (Résumé en français de la section soixante-dix-huit, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.)
«- The signature gotcha: nested routes use the deepest route’s meta and
drop parent meta unless you merge it with the matches argument.
» (Traduction) (Résumé en français de la section soixante-dix-huit, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.)
«- loader() runs server-side; throw new Response("", { status: 404 }) gives a
real 404 (no soft 404) and throw redirect() gives a real 301/302.
» (Traduction) (Résumé en français de la section soixante-dix-huit, sous-partie quatre : le texte source est conservé pour vérification lors de la relecture francophone.)
«- headers() sets Cache-Control (TTFB/CWV) and X-Robots-Tag at the HTTP level
— works even for crawlers that don’t read the body.
» (Traduction) (Résumé en français de la section soixante-dix-huit, sous-partie cinq : le texte source est conservé pour vérification lors de la relecture francophone.)
«- links() vs meta() for canonicals: links() is static (no loader data);
meta() with tagName: "link" is dynamic. Static → links(), dynamic → meta().
» (Traduction) (Résumé en français de la section soixante-dix-huit, sous-partie six : le texte source est conservé pour vérification lors de la relecture francophone.)
«- ErrorBoundary renders inside the surviving layout and pairs with thrown 404s
for correct status + usable page.
» (Traduction) (Résumé en français de la section soixante-dix-huit, sous-partie sept : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Remix v2’s APIs merged into React Router v7 (Dec 2024) — same SEO APIs,
renamed packages (react-router), new routes.ts, optional static
pre-rendering, Vite bundling. React Router is now on v8 (June 2026; v7 still
security-supported). Separately, Remix 3 (beta) is a newer, distinct
full-stack framework — not just React Router under the old name. This article
covers the Remix v2 / React Router v7-v8 route API. No dynamic rendering tools
needed for that API either way.
» (Traduction) (Résumé en français de la section soixante-dix-huit, sous-partie huit : le texte source est conservé pour vérification lors de la relecture francophone.)
Documentation officielle
Primary-source documentation depuis Remix / React Router et le moteur de recherches.
Remix / React Router
- Remix —
metafunction — per-route meta balises, descriptors, etmatchesmerging. - Remix —
loaderfunction — serveur-side données fetching et thrown réponses. - Remix —
headersfunction — per-route HTTP réponse headers. - Remix —
linksfunction —<link>elements (canonical, preload, favicon). - Remix —
ErrorBoundary— route et root erreur handling. - React Router v7 announcement — le Remix v2 → React Router merge (Dec 2024).
- React Router v8 — le actuel release (June 2026) et le “one team, two projects” remarque on comment Remix 3 maintenant differs depuis React Router.
- React Router docs — où le Remix v2-lineage route API maintenant lives, currently on v8.
- Comprendre le JavaScript SEO basics — le three-phase explorer/rendre/indexer traiter, code d’états,
<a href>liens, et SSR guidance. - Dynamic rendu (deprecated) — pourquoi dynamic rendu est a workaround, pas a solution.
- Dans-Depth Guide à Comment Recherche Google Fonctionne — où rendu sits dans explorer → indexer → servir.
Quotes from the source
On-le-record statements depuis Google que bear directement on Remix’s SSR-premier model. Chaque recherche-moteur lien est a deep lien que jumps à le quoted passage.
Google — pourquoi SSR helps
«- “Server-side or pre-rendering is still a great idea because it makes your website faster for users and crawlers, and not all bots can run JavaScript.”
Jump to quote
» (Traduction) (Résumé en français de la section quatre-vingt-dix, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
«- “Google can only discover your links if they are <a> HTML elements with an href attribute.”
Jump to quote
» (Traduction) (Résumé en français de la section quatre-vingt-dix, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.)
Google — code d’états (the loader + ErrorBoundary pattern)
- “À tell Googlebot si a page ne peut pas être exploré or indexé, utiliser a meaningful code d’état, like a 404 pour a page ce pourrait pas être trouvé or a 401 code pour pages behind a login.” Jump à citation
Google — dynamic rendu est deprecated
- “Dynamic rendu était a workaround et pas a à long terme solution pour problèmes avec JavaScript-generated contenu dans moteurs de recherche.” Jump à citation
Remix SEO checklist
A rapide réussir pour quelconque Remix / React Router v7 project:
- Pages importantes sont serveur-rendu (le par défaut) — confirmer contenu est dans
View Source /
curl, pas seulement le post-hydration DOM. - Chaque indexable route sets a
<title>et meta description via lemeta()export. - Child routes que export
meta()merge parent meta via lematchesargument (aucun dropped site-wide balises). - Manquant resources
throw new Response(..., { status: 404 })dans leloader— vérifier a réel 404, pas a soft 404 (200 + “pas trouvé”). - Déplacé URLs
throw redirect(target, 301)depuis le loader — réel 301/302. - Balise canonicals sont définir dans HTML:
links()pour static,meta()avectagName: "link"pour dynamic (paginated/filtered/param URLs). -
noindexest définir dans le SSR’d<head>or viaheaders()X-Robots-Tag— jamais client-side seulement. -
headers()sets sensibleCache-Controlpour CDN mise en cache / TTFB on leaf routes. - JSON-LD est ajouté via
{ "script:ld+json": {...} }dansmeta()et validated dans le Résultats enrichis Tester. -
sitemap.xmletrobots.txtexist as resource routes (sitemap[.xml].tsx,robots[.txt].tsx) et sont submitted dans Search Console. - Lien internes utiliser Remix
<Link>(renders réel<a href>). - Aucun
@remix-run/*→react-routerrename surprises si vous êtes migrating à v7.
The mental models
«1. SSR is the floor, not the ceiling.
Remix renders on the server by default, which removes the discovery/rendering risk
class for free. It does not auto-handle titles, descriptions, canonicals,
structured data, cache headers, or 404 status codes — each is an explicit meta(),
headers(), links(), or loader decision. SSR is a prerequisite, not a complete
SEO solution.
» (Traduction) (Résumé en français de la section cent quatre, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
2. Le correct export pour le correct signal.
- HTML
<head>balises (titre, description, OG, JSON-LD, dynamic canonical) →meta() - Static
<link>elements (favicon, preload, static canonical) →links() - Code d’état HTTPs (404, 301, 302) et serveur données →
loader() - HTTP réponse headers (
Cache-Control,X-Robots-Tag) →headers() - Erreur/404 page bodies →
ErrorBoundary
3. meta() beats links() quand données matters.
links() can’t voir loader données; meta() peut. So anything que dépend on récupéré
content — a dynamic canonical, a data-driven title — belongs in meta().
4. Le nesting par défaut est “deepest wins, parents dropped.”
Le décision règle: quelconque child que exports meta() doit consciously choisir à merge
parent meta (matches) or il loses il. Put universal balises (charset/viewport) dans
root.tsx JSX où merging ne peut pas strip les.
5. Réel code d’états sont an SEO fonctionnalité, pas an accident.
throw new Response(..., { status: 404 }) et throw redirect() sont comment Remix
avoids le soft-404 / fake-redirect échecs de CSR SPAs. Reach pour thrown Réponses
au lieu de rendu an message d’erreur à a 200.
Remix SEO — cheat sheet
Per-route exports → ce que ils contrôler
| Export | Contrôle | Voit loader données? | SEO utiliser |
|---|---|---|---|
meta() | <head> meta/titre/JSON-LD | Oui | Titles, descriptions, OG, JSON-LD, dynamic canonical |
links() | <link> elements | Aucun | Static canonical, favicon, preload |
loader() | Serveur données + status | n/a (il est le données) | Réel 404/301, serveur-seulement fetching |
headers() | HTTP réponse headers | via loaderHeaders | Cache-Control, X-Robots-Tag |
ErrorBoundary | Erreur/404 page corps | via useRouteError | Usable 404 page dans-layout |
Balise canonicals — pick the correct un
| Besoin | Utiliser |
|---|---|
| Static, connu at construire | links() → { rel: "canonical", href } |
| Dynamic (dépend on loader données) | meta() → { tagName: "link", rel: "canonical", href } |
Code d’états from a loader
throw new Response("Not found", { status: 404 }); // real 404
throw redirect("/new-url/", 301); // real 301«Fast facts
» (Traduction) (Résumé en français de la section cent dix-huit, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Remix is SSR by default — content in the initial HTML, no render queue
(confirm the deployed mode; static prerender/SPA are also configuration options).
» (Traduction) (Résumé en français de la section cent dix-huit, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Nested meta: deepest route wins; merge parents via matches or lose them.
» (Traduction) (Résumé en français de la section cent dix-huit, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.)
«- X-Robots-Tag via headers() works for crawlers that don’t render.
» (Traduction) (Résumé en français de la section cent dix-huit, sous-partie quatre : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Remix v2’s APIs merged into React Router v7 (Dec 2024), same route API,
package renamed to react-router. React Router is now on v8 (v7 still
security-supported). Remix 3 (beta) is a separate, newer framework — not
covered by this page.
» (Traduction) (Résumé en français de la section cent dix-huit, sous-partie cinq : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Resource routes: sitemap[.xml].tsx, robots[.txt].tsx (brackets escape the dot).
» (Traduction) (Résumé en français de la section cent dix-huit, sous-partie six : le texte source est conservé pour vérification lors de la relecture francophone.)
«- No dynamic rendering tools (Rendertron/Prerender.io) needed — or recommended.
» (Traduction) (Résumé en français de la section cent dix-huit, sous-partie sept : le texte source est conservé pour vérification lors de la relecture francophone.)
Vérifier ce que robots d’exploration en réalité recevoir
Remix renders on le serveur, mais toujours confirmer le initial HTML plutôt que le post-hydration DOM dans DevTools.
Vérifier le raw serveur HTML (contenu + balises sont présent avant JS exécute)
# Title, meta description, canonical — should all be in the raw response
curl -s https://example.com/blog/my-post/ | grep -iE '<title>|name="description"|rel="canonical"'
# Confirm JSON-LD is in the SSR'd head
curl -s https://example.com/blog/my-post/ | grep -i 'application/ld+json'Confirmer réel Code d’état HTTPs (aucun soft 404s, réel redirections)
# A missing page must return 404, not 200
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/blog/does-not-exist/
# A moved URL should return 301/302 with a Location header
curl -sI https://example.com/old-url/ | grep -iE 'HTTP/|location'Vérifier HTTP-level robots / cache headers depuis headers()
curl -sI https://example.com/private-route/ | grep -iE 'x-robots-tag|cache-control'«## A minimal robots.txt resource route » (Traduction) (Résumé en français de la section cent vingt-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
// app/routes/robots[.txt].tsx
export async function loader() {
return new Response(
`User-agent: *\nAllow: /\nSitemap: https://example.com/sitemap.xml\n`,
{ headers: { "Content-Type": "text/plain" } }
);
} Outils pour auditing a Remix site
curl/ View Source — le ground truth pour qu’est-ce que dans le initial SSR’d HTML (titles, description, canonical, JSON-LD) avant hydration. ne pas confiance DevTools Elements alone — il montre le post-JS DOM.- Recherche Google Console — Inspection d’URL — voir comment Googlebot exploré et rendu a unique Remix URL; pour SSR pages le servi et rendu HTML devrait match.
- Résultats enrichis Tester — valider le JSON-LD vous emit via
"script:ld+json". - Ahrefs Site Audit — explorer le entier site pour manquant/duplicate meta (le nested-meta merge bug surfaces ici), broken canonicals, chaîne de redirectionss, et depth.
- Screaming Frog SEO Spider — a developer-friendly explorer à confirmer titles, canonicals, et code d’états à travers routes.
vite-bundle-visualizer— inspect client bundles (Remix utilise Vite) à garder JS lean pour Core Web Vitals.- Bing Webmaster Outils — Inspection d’URL — vérifier Bingbot voit le SSR’d contenu.
Testez vos connaissances: Remix SEO
Five rapide questions on how Remix handles SEO. Pick an réponse pour chaque, alors vérifier.
Ressources utiles
«My writing » (Traduction) (Résumé en français de la section cent quarante-trois, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.) «- JavaScript SEO: A Definitive Guide — rendering, DOM parity, the most-restrictive-directive rule, and choosing a rendering mode — the framework-agnostic background for everything Remix does by default. » (Traduction) (Résumé en français de la section cent quarante-trois, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.) «- The Beginner’s Guide to Technical SEO — where rendering and frameworks fit in the bigger picture. » (Traduction) (Résumé en français de la section cent quarante-trois, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.)
Mon speaking
- Comment Recherche Fonctionne (SlideShare) — mon walkthrough de exploration, rendu, indexation, et classement. (Mon standing disclaimer s’applique: “This is my understanding of systems… not going to be 100% complete or accurate.”)
«From around the industry
» (Traduction) (Résumé en français de la section cent quarante-cinq, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Remix docs — meta function — primary source for per-route metadata and matches merging.
» (Traduction) (Résumé en français de la section cent quarante-cinq, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Remix docs — loader and headers — server data, thrown responses, and HTTP headers.
» (Traduction) (Résumé en français de la section cent quarante-cinq, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.)
«- React Router v7 announcement / React Router v8 / React Router docs — the Remix v2 merge, the current v8 release, and where the route API now lives (Remix 3 is a separate, newer product).
» (Traduction) (Résumé en français de la section cent quarante-cinq, sous-partie quatre : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Understand the JavaScript SEO basics (Google) — the rules Remix’s output is judged by: status codes, <a href> links, SSR guidance.
» (Traduction) (Résumé en français de la section cent quarante-cinq, sous-partie cinq : le texte source est conservé pour vérification lors de la relecture francophone.)
«- Dynamic rendering (deprecated) (Google) — why Remix sites never need Rendertron/Prerender.io.
» (Traduction) (Résumé en français de la section cent quarante-cinq, sous-partie six : le texte source est conservé pour vérification lors de la relecture francophone.)
«- web.dev — Rendering on the Web — the Chrome team’s CSR/SSR/SSG/hydration explainer; the conceptual backbone for Remix’s server-first model.
» (Traduction) (Résumé en français de la section cent quarante-cinq, sous-partie sept : le texte source est conservé pour vérification lors de la relecture francophone.)
«- r/TechSEO — the community for framework rendering/indexing debugging.
» (Traduction) (Résumé en français de la section cent quarante-cinq, sous-partie huit : le texte source est conservé pour vérification lors de la relecture francophone.)
Journal des modifications
Mis à jour le 18 juil. 2026.
Résumé éditorial et détails enregistrés des changements.Détails des changements
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
-
Les notes détaillées des changements sont actuellement disponibles en anglais.
Comparaison complète indisponible — aucun instantané antérieur n’a été archivé pour cette révision.