T
TanStack4w ago
genetic-orange

SEO in TanStack Router

Hey there, can anyone help me with the right approach for SEO in TanStack Router, please? I mostly want SPA for my app, but then there are some parts like the public course page (https://learn.self-host.tech/courses/83311579-014d-4b50-ab58-d8e78d576426 - https://github.com/catalinpit/learn-platform/blob/main/client/src/routes/courses/%24courseId.tsx) that I want to appear in search engines. How should I go about this? Or should I "upgrade" to TanStack Start?
35 Replies
other-emerald
other-emerald4w ago
Hey, are you familiar with the head prop in createFileRoute() and createRootRoute()? https://tanstack.com/router/v1/docs/framework/react/guide/document-head-management You want to do all your API calls in the loader (such as fetching the image, title and description). Then you can get the loaderData in your head params
head: ({ loaderData, params: { postid, commentid } }) => {
const imageUrl = loaderData?.author?.avatar;

return {
meta: [
{
title: loaderData?.comment?.title,
},
{
description: loaderData?.comment?.description,
},
{
property: "og:title",
content: loaderData?.comment?.title,
},
{
property: "og:description",
content: loaderData?.comment?.description,
},
{
property: "og:type",
content: "website",
},
{
property: "og:image",
content: imageUrl,
},
{
property: "og:url",
content: `https://mygreatsite.com/${postid}/comment/${commentid}`,
},
{
property: "og:site_name",
content: "My Great Site",
},
],
};
},
head: ({ loaderData, params: { postid, commentid } }) => {
const imageUrl = loaderData?.author?.avatar;

return {
meta: [
{
title: loaderData?.comment?.title,
},
{
description: loaderData?.comment?.description,
},
{
property: "og:title",
content: loaderData?.comment?.title,
},
{
property: "og:description",
content: loaderData?.comment?.description,
},
{
property: "og:type",
content: "website",
},
{
property: "og:image",
content: imageUrl,
},
{
property: "og:url",
content: `https://mygreatsite.com/${postid}/comment/${commentid}`,
},
{
property: "og:site_name",
content: "My Great Site",
},
],
};
},
Document Head Management | TanStack Router React Docs
Document head management is the process of managing the head, title, meta, link, and script tags of a document and TanStack Router provides a robust way to manage the document head for full-stack appl...
other-emerald
other-emerald4w ago
Check out https://ogp.me/ for opengraph info
Open Graph protocol
The Open Graph protocol enables any web page to become a rich object in a social graph.
other-emerald
other-emerald4w ago
For the og:url prop, you can even add query params if your different views are dependent on search params. From what I understand, the url prop's job is to provide a clean, canonical url that search engines should use to identify the page, without any extra query params that might come from all sorts of filters or the like
stormy-gold
stormy-gold4w ago
But you still need tanstack start for it right? Otherwise it will still be server as SPA
other-emerald
other-emerald4w ago
Google crawlers do render components and crawl pages even in a SPA - but I'm not sure what exactly is the threshold between "this is ok to render" and "man, this is too expensive, I give up". For SEO, beware of any errors from network calls, regardless of whether the app is purely client or server side rendered - for a really long time my production application used sessionStorage in server rendered components, and I couldn't find a nice way to handle it properly. I got the classic "window/localStorage/sessionStorage is not defined" that you'll run into in apps with SSR. This results in a 500 status code from the server. But from the user's perspective, there have been zero problems. App is usable exactly as intended. However, the crawler sees the 500 and thinks "damn, that sucks, guess I'll just come back later", and doesn't even index the page
genetic-orange
genetic-orangeOP4w ago
Thanks for the messages! Waiting to see more opinions to decide
vicious-gold
vicious-gold4w ago
there are a few options 1. SSR this can be done with start or with just router alone (see https://tanstack.com/router/latest/docs/framework/react/guide/ssr) you can also choose which routes to SSR, see https://tanstack.com/start/latest/docs/framework/react/selective-ssr 2. prerender this can be done out of the box with start: https://tanstack.com/start/latest/docs/framework/react/static-prerendering depends on how static your data is can be nicely combined with SPA mode: https://tanstack.com/start/latest/docs/framework/react/spa-mode
genetic-orange
genetic-orangeOP4w ago
Thank you! So the best bet would be to use TanStack Start?
vicious-gold
vicious-gold4w ago
Start is Router with SSR and some server addons such as API routes, server functions etc if you dont need the server addons, router with SSR could work too
genetic-orange
genetic-orangeOP4w ago
Dumb question, but if I have a separate backend, Router with SSR should be enough, right? Or?
vicious-gold
vicious-gold4w ago
not dumb at all and yes, that's what I meant. if you dont need a "backend" then router should be enough there are a few niceties in start such as createIsomorphicFn for example that makes you life easier and you can also just use start without using any of the "backend" things
genetic-orange
genetic-orangeOP4w ago
I'm tempted to pick TanStack Start then. My only worry is the changes that will happen once it goes stable Sorry to bother you so much, but I can't really wrap my head around doing this while keeping the auth context and all that stuff I had previously in main https://github.com/catalinpit/learn-platform/pull/20/files
vicious-gold
vicious-gold4w ago
there will be changes in the vite configuration, but nothing major really
genetic-orange
genetic-orangeOP4w ago
If I'm not able to solve the above issue, I'll simply move to TSS then
genetic-orange
genetic-orange4w ago
I use TanstackRouter and for the moment createHtmlPlugin for Vite to inject some tile, description and image. My SPA should only be able to look nice when links are shared within socials like WhatsApp messages or the og:image should be visible when a link is shared via Facebook or anything else. I tried to move my Router to <HeadContent/> but this does not seem to work - when I share now links the infos are not available. Using both, <HeadContent/> and createHtmlPlugin results in duplication of Head data. Is only Start possible to make this work? Would the SPA mode make it still possible to have the head section filled with data?
vicious-gold
vicious-gold4w ago
you can only use head content really if you render the whole html with react and do full document hydration
genetic-orange
genetic-orange4w ago
Thanks for your answer - does this mean Start is the answer? At least I am not awar how to do full document hydration with Tanstack Router alone?
vicious-gold
vicious-gold4w ago
did you have a look at https://tanstack.com/router/latest/docs/framework/react/guide/ssr ? you need SSR for this
SSR | TanStack Router React Docs
[!WARNING] While every effort has been made to separate these APIs from changes to Tanstack Start, there are underlying shared implementations internally. Therefore these can be subject to change and...
vicious-gold
vicious-gold4w ago
but not necessarily start maybe it's possible without SSR even
genetic-orange
genetic-orange4w ago
Ok this would mean I would need a server proxy and serve my SPA similar to how Prerender.io would work that would be my goal - I simply just want that the initial page render includes the HTML head correctly that shared links getting the bare necessary data to look nice being shared
vicious-gold
vicious-gold4w ago
sounds like prerendering yes. have a look at static prerenderig and spa mode in start both combined should do what you need
genetic-orange
genetic-orange4w ago
is there a migration doc out there for users of Router moving to Start? I didnt find it last time I looked for it. This is my current config
tanstackRouter({
routesDirectory: "src/routes/web",
generatedRouteTree: "src/routes/webRouteTree.gen.ts",
quoteStyle: "double",
autoCodeSplitting: true
}),
tanstackRouter({
routesDirectory: "src/routes/web",
generatedRouteTree: "src/routes/webRouteTree.gen.ts",
quoteStyle: "double",
autoCodeSplitting: true
}),
I guess moving to Start first I have to adjust my file structure for it? I only found the one for NextJS
vicious-gold
vicious-gold4w ago
we don't have that yet. you could try this and skip the parts you already did https://tanstack.com/start/latest/docs/framework/react/build-from-scratch
Build a Project from Scratch | TanStack Start React Docs
[!NOTE] If you chose to quick start with an example or cloned project, you can skip this guide and move on to the guide. So you want to build a TanStack Start project from scratch? This guide will hel...
vicious-gold
vicious-gold4w ago
file structure does not need to be changed, you just need to expose router creation in a separate file
genetic-orange
genetic-orange4w ago
where does the config of quoteStyle and generatedRouteTree goes? Do I keep the tanstackRouter config just inside the vite config beside the Start config?
vicious-gold
vicious-gold4w ago
inside tsr of the start vite plugin
genetic-orange
genetic-orange4w ago
Ok thanks I will give it a try I got one more question about this topic: as all my routes are behind authentication I want to only open one very specific route to be SSR rendered. The initial and shared SEO related things can be within ENV variables - only this one specific route should be sending fetched data from the server to the client. Is that a perfect example for selective SSR? Would defautlSsr:false in the router config and only one one specific route ssr: true work for this? Would that work with the Vercel plugin of TSS?
genetic-orange
genetic-orangeOP4w ago
I'm writing one as I'm doing the migration right now
vicious-gold
vicious-gold4w ago
would love to have that in the start docs! did you have a look at the selective SSR docs?
genetic-orange
genetic-orangeOP4w ago
Will let you know when it’s done. If you like it, I can add it
genetic-orange
genetic-orange3w ago
Yes I read the docs but still I am not sure if this means all SSR:false routes are static HTML/JS/CSS? I only have the need of SSR one path with user generated public exercises /exercises-db/$exerciseId - prerendering would not cut it as its dynamic user generated content. I delivered the feature for now without og:image from the user generated content but will try selective SSR.
vicious-gold
vicious-gold3w ago
all SSR:false routes are static HTML/JS/CSS
no. you would need to prerender them
vicious-gold
vicious-gold3w ago
Static Prerendering | TanStack Start React Docs
Static prerendering is the process of generating static HTML files for your application. This can be useful for either improving the performance of your application, as it allows you to serve pre-rend...
genetic-orange
genetic-orange3w ago
thanks I hope I find some time soon to give it a shot
genetic-orange
genetic-orangeOP3w ago
I'm not sure if anything from here is helpful for the docs, but here's the draft 🙏 https://catalins.tech/p/c0a56185-2dc7-4ca9-bbed-ed42540cd23c/
Catalin's Tech
Migrating to TanStack Start
A while ago, I decided to break away from full-stack React frameworks and use a client-server architecture. That is, separate the backend and the frontend, each being a standalone app. So, I picked the following stack: * Backend: Hono + Bun + PostgreSQL + Zod + Prisma + TypeScript + Better Auth * Frontend: React + Vite + TypeScript + TanStack

Did you find this page helpful?