Skip to content

Start hydration issue with localized head meta title using Lingui i18n #4279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
longzheng opened this issue May 30, 2025 · 8 comments
Open

Comments

@longzheng
Copy link

longzheng commented May 30, 2025

Which project does this relate to?

Start

Describe the bug

When using the Lingui i18n localization + TanStack Start template, when trying to use a localized meta title the page hydrates weirdly.

import { i18n } from "@lingui/core";
import { msg } from "@lingui/core/macro";

export const Route = createFileRoute("/")({
  component: Home,
  head: () => {
    return {
      meta: [
        {
          title: i18n._(msg`Test English title`),
        },
      ],
    };
  },
});

Before hydration, the SSR render is all correct. The page title is localized and the content is correct.

After hydration, the contents appears twice, the top appears to be the SSR version (clicking links will load pages), the bottom appears to be the hydrated client (clicking links do not load pages). The localized title also does not work because there are two <title> in the HTML

<head>
   <title>TanStack Start | Type-Safe, Client-First, Full-Stack React Framework</title>
   <title>Test English title</title>

There are also no hydration errors in the console.

Your Example Website or App

https://codesandbox.io/p/devbox/nice-snowflake-yrx273?file=%2Fsrc%2Froutes%2Findex.tsx%3A3%2C1-4%2C42&workspaceId=ws_62aAsWkSzvrQC9UJ9wAyfv

Steps to Reproduce the Bug or Issue

  1. Go to the sandbox
  2. Open the preview in a new window (to see the page title)
  3. Iinitially the page title will be correct in the SSR version
  4. Wait a few seconds for the client hydration
  5. Page title will be incorrect and content will be doubled

Expected behavior

The page to be hydrated correctly, no double up.
The page title to be localized.

Screenshots or Videos

SSR before hydration

Image

After hydration

Image

Platform

  • OS: Windows
  • Browser: Chrome
  • Version: 137

Additional context

No response

@schiller-manuel
Copy link
Contributor

does this still exist on the alpha branch?

@longzheng
Copy link
Author

longzheng commented May 30, 2025

does this still exist on the alpha branch?

I haven't been following the alpha branch, I can't quite get it to start, defineConfig got moved? Is there a guide somewhere with the breaking changes?

EDIT: Ah I saw the examples at https://github.com/TanStack/router/tree/alpha/examples/react/start-basic but I'm not seeing a way how to do custom client/server initialization

@schiller-manuel
Copy link
Contributor

checkout #2863 (comment)

@longzheng
Copy link
Author

checkout #2863 (comment)

Sorry I'm a bit confused how defineEventHandler is supposed to be used now? It looks like createStartHandler no longer takes a event: H3Event<EventHandlerRequest>?

import { i18n } from "@lingui/core";
import {
  createStartHandler,
  defaultStreamHandler,
  defineEventHandler,
} from "@tanstack/react-start/server";

import { createRouter } from "./router";
import { setupLocaleFromRequest } from "./modules/lingui/i18n.server";

export default defineEventHandler(async (event) => {
  await setupLocaleFromRequest();

  return createStartHandler({
    createRouter: () => {
      return createRouter({ i18n });
    },
  })(defaultStreamHandler)(event);
});

Also how do I customize the client.tsx now with custom logic?

import { i18n } from "@lingui/core";
import { hydrateRoot } from "react-dom/client";
import { StartClient } from "@tanstack/react-start";
import { dynamicActivate } from "./modules/lingui/i18n";

import { createRouter } from "./router";

// The lang should be set by the server
dynamicActivate(document.documentElement.lang);

const router = createRouter({ i18n });

hydrateRoot(document, <StartClient router={router} />);

@schiller-manuel
Copy link
Contributor

have a look at our e2e example on how server and client can be customized

https://github.com/TanStack/router/tree/alpha/e2e%2Freact-start%2Fbasic%2Fsrc

@paolostyle
Copy link

That link doesn't help at all, server.ts is just the default mentioned in the docs.

The docs here (I found it on X): https://tanstack-com.vercel.app/start/latest/docs/framework/react/server-routes#handling-server-route-requests mention a defineHandler function which doesn't exist and like @longzheng mentioned, createStartHandler no longer accepts an event from defineEventHandler.

I'm not 100% sure if the approach in Lingui's repo is the best solution to this problem, but basically I'd like to execute some logic on every route in the app, server side, based on the request headers. That might affect how router is rendered and might be async. How do I do that?

@schiller-manuel
Copy link
Contributor

@paolostyle what exactly do you need to execute here? is this inside of react?

@paolostyle
Copy link

paolostyle commented May 31, 2025

For now I'd like to just make the code posted in OP's last comment work:

import { i18n } from "@lingui/core";
import {
  createStartHandler,
  defaultStreamHandler,
  defineEventHandler,
} from "@tanstack/react-start/server";

import { createRouter } from "./router";
import { setupLocaleFromRequest } from "./modules/lingui/i18n.server";

export default defineEventHandler(async (event) => {
  await setupLocaleFromRequest();

  return createStartHandler({
    createRouter: () => {
      return createRouter({ i18n });
    },
  })(defaultStreamHandler)(event);
});

setupLocaleFromRequest is defined here. But you might as well simplify it to await Promise.resolve() and remove passing the i18n to createRouter; it's just that I want to be able to await an async function BEFORE createStartHandler.

At the moment it does not work, because of TypeScript error Argument of type 'H3Event<EventHandlerRequest>' is not assignable to parameter of type '{ request: Request; }'. when trying to pass event at the very end; it also doesn't work in runtime.

For essentially the same reason e.g. @clerk/tanstack-react-start does not work in the devinxi/alpha version, at least on 1.121.0-alpha.21. TypeScript won't complain in your official example because you @ts-expect-errored it, but if you try to run pnpm dev on this example: https://github.com/TanStack/router/tree/v1.121.0-alpha.21/examples/react/start-clerk-basic you'll get An error occured while server rendering /index.html: No HTTPEvent found in AsyncLocalStorage. Make sure you are using the function within the server runtime..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants