T
TanStack2mo ago
rare-sapphire

Run code before SSR and CSR in RC

I'm upgrading to RC from 1.120. Prior RC, I had i18n setup with lingui similar to https://github.com/lingui/js-lingui/tree/main/examples/tanstack-start
// ssr.ts
/// <reference types="vinxi/types/server" />
import { i18n } from "@lingui/core"
import {
createStartHandler,
defaultStreamHandler,
defineEventHandler,
} from "@tanstack/react-start/server"
import { getRouterManifest } from "@tanstack/react-start/router-manifest"

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

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

return createStartHandler({
createRouter: () => {
return createRouter({ i18n })
},
getRouterManifest,
})(defaultStreamHandler)(event)
})
// ssr.ts
/// <reference types="vinxi/types/server" />
import { i18n } from "@lingui/core"
import {
createStartHandler,
defaultStreamHandler,
defineEventHandler,
} from "@tanstack/react-start/server"
import { getRouterManifest } from "@tanstack/react-start/router-manifest"

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

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

return createStartHandler({
createRouter: () => {
return createRouter({ i18n })
},
getRouterManifest,
})(defaultStreamHandler)(event)
})
// __root.tsx
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html lang={i18n.locale}>
// __root.tsx
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html lang={i18n.locale}>
// client.ts
/// <reference types="vinxi/types/client" />
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} />)
// client.ts
/// <reference types="vinxi/types/client" />
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} />)
In RC, client.ts is the same but I don't know how to inject it to server.ts The problem I want to solve is to call dynamicActivate (which internally imports messages and sets the active locale) before SSR and CSR I considered global middleware at start.ts (1/2)
GitHub
js-lingui/examples/tanstack-start at main · lingui/js-lingui
🌍 📖 A readable, automated, and optimized (2 kb) internationalization for JavaScript - lingui/js-lingui
19 Replies
rare-sapphire
rare-sapphireOP2mo ago
// start.ts
import { createMiddleware, createStart } from "@tanstack/react-start";
import { dynamicActivate } from "./i18n/i18n";
import { getLocaleFromRequest } from "./i18n/server";

const localeMiddleware = createMiddleware().server(
async ({ request, next }) => {
await dynamicActivate(getLocaleFromRequest(request));
return next();
}
);

export const startInstance = createStart(() => {
return {
requestMiddleware: [localeMiddleware],
};
});
// start.ts
import { createMiddleware, createStart } from "@tanstack/react-start";
import { dynamicActivate } from "./i18n/i18n";
import { getLocaleFromRequest } from "./i18n/server";

const localeMiddleware = createMiddleware().server(
async ({ request, next }) => {
await dynamicActivate(getLocaleFromRequest(request));
return next();
}
);

export const startInstance = createStart(() => {
return {
requestMiddleware: [localeMiddleware],
};
});
But the docs stats:
Global request middleware runs before every request, including both server routes and server functions.
I've no idea whether SSR is included by this or not.
fascinating-indigo
fascinating-indigo2mo ago
yes it is docs need updating here
rare-sapphire
rare-sapphireOP2mo ago
the global middleware is correct? And is there a better way to await an async (import messages) before CSR?
fascinating-indigo
fascinating-indigo2mo ago
better than what?
rare-sapphire
rare-sapphireOP2mo ago
// client.ts
import { StartClient } from "@tanstack/react-start/client";
import { hydrateRoot } from "react-dom/client";
import type { Lang } from "@/i18n/i18n";
import { dynamicActivate } from "./i18n/i18n";

void dynamicActivate(document.documentElement.lang as Lang);

hydrateRoot(document, <StartClient />);
// client.ts
import { StartClient } from "@tanstack/react-start/client";
import { hydrateRoot } from "react-dom/client";
import type { Lang } from "@/i18n/i18n";
import { dynamicActivate } from "./i18n/i18n";

void dynamicActivate(document.documentElement.lang as Lang);

hydrateRoot(document, <StartClient />);
fascinating-indigo
fascinating-indigo2mo ago
so you can either do this - in getRouter (by making getRouter an isomorphic function via createIsomorphicFn) - or in hydrate (function passed into createRouter())
rare-sapphire
rare-sapphireOP2mo ago
Could getRouter be async? Is hydrate called before or after hydration? I’m asking because I read the locale from window.documentElement.lang. The same question applies to the isomorphic fn in getRouter: how can I pass the locale from the server (extracted from the request) to the client and then use it in dynamicActivate?
fascinating-indigo
fascinating-indigo2mo ago
yes getRouter can be async hydrate is called before react hydration happens (or rather, react hydration suspends as long as router is still initializing)
The same question applies to the isomorphic fn in getRouter: how can I pass the locale from the server (extracted from the request) to the client and then use it in dynamicActivate?
so you dont want to read it of window then on the client?
rare-sapphire
rare-sapphireOP2mo ago
I read it from window because I set <html lang={serverLocale}> on the server, but If hydrate is called before react hydration, will the html attribute be available on window? sorry I got confused react hydration and browser loading JS. the dom attribute will be available on window.documentElement.lang before even react hydration. Thank you so much for your help Manuel
fascinating-indigo
fascinating-indigo2mo ago
sure! you can still send it to the client btw you can return an object from dehydrate (runs on the server) and then this will be passed into hydrate
rare-sapphire
rare-sapphireOP2mo ago
the api looks so cool
const router = createTanStackRouter({
dehydrate: () => ({ lang: getLocaleFromRequest() }),
hydrate: (dehydrated) => dynamicActivate(dehydrated.lang),
const router = createTanStackRouter({
dehydrate: () => ({ lang: getLocaleFromRequest() }),
hydrate: (dehydrated) => dynamicActivate(dehydrated.lang),
Except it does this
✓ 3166 modules transformed.
✗ Build failed in 7.93s
error during build:
[vite]: Rollup failed to resolve import "tanstack-start-injected-head-scripts:v" from "/home/ahmed/projects/63-basmah/node_modules/@tanstack/start-server-core/dist/esm/loadVirtualModule.js".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
at viteLog (file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:33989:57)
at file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:34025:73
at onwarn (file:///home/ahmed/projects/63-basmah/node_modules/@vitejs/plugin-react/dist/index.mjs:104:9)
at file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:34025:28
at onRollupLog (file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:34020:63)
at onLog (file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:33821:4)
at file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:20937:32
at Object.logger [as onLog] (file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:22823:9)
at ModuleLoader.handleInvalidResolvedId (file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:21567:26)
at ModuleLoader.resolveDynamicImport (file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:21625:58)
at async file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:21509:32
 ELIFECYCLE  Command failed with exit code 1.
✓ 3166 modules transformed.
✗ Build failed in 7.93s
error during build:
[vite]: Rollup failed to resolve import "tanstack-start-injected-head-scripts:v" from "/home/ahmed/projects/63-basmah/node_modules/@tanstack/start-server-core/dist/esm/loadVirtualModule.js".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
at viteLog (file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:33989:57)
at file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:34025:73
at onwarn (file:///home/ahmed/projects/63-basmah/node_modules/@vitejs/plugin-react/dist/index.mjs:104:9)
at file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:34025:28
at onRollupLog (file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:34020:63)
at onLog (file:///home/ahmed/projects/63-basmah/node_modules/vite/dist/node/chunks/dep-Chhhsdoe.js:33821:4)
at file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:20937:32
at Object.logger [as onLog] (file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:22823:9)
at ModuleLoader.handleInvalidResolvedId (file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:21567:26)
at ModuleLoader.resolveDynamicImport (file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:21625:58)
at async file:///home/ahmed/projects/63-basmah/node_modules/rollup/dist/es/shared/node-entry.js:21509:32
 ELIFECYCLE  Command failed with exit code 1.
Is that expected? wrapping dehydrate in a createServerFn fixes it
fascinating-indigo
fascinating-indigo2mo ago
although dehydrate runs only on the server, it still must be isomorphic as getRouter must be isomorphic so you can either - supply different impls of getRouter via createIsomorphicFn - wrap the function you pass into dehydrate with createServerOnlyFn
xenial-black
xenial-black2mo ago
Hello have you solve this ?
fascinating-indigo
fascinating-indigo2mo ago
solve what? can you please create a new question including a detailed description of what you want to do?
xenial-black
xenial-black2mo ago
I tried following the Lingui setup tutorial with TanStack, but it seems outdated. How can I set up Lingui with TanStack Start and enable dynamic language loading? Does TanStack support .po files?
fascinating-indigo
fascinating-indigo2mo ago
no, why would it? thats what i18n libraries/plugins are for
xenial-black
xenial-black2mo ago
in lingui po file has transformation babel but I can’t find properly way to load and activate locale
xenial-black
xenial-black2mo ago
https://github.com/chanphiromsok/tanstack-lingui Now I got error hydration and still not load proper yet
GitHub
GitHub - chanphiromsok/tanstack-lingui
Contribute to chanphiromsok/tanstack-lingui development by creating an account on GitHub.
xenial-black
xenial-black2mo ago
issue resolved

Did you find this page helpful?