feat: add TanStack Start example app with gt-tanstack-start#1080
feat: add TanStack Start example app with gt-tanstack-start#1080moss-bryophyta wants to merge 1 commit intogeneraltranslation:mainfrom
Conversation
Demonstrates gt-tanstack-start with local translations: - initializeGT + GTProvider setup in root route - getTranslations() in root loader for SSR - <T> components for automatic JSX translation - LocaleSelector for language switching - Local translation files (es, ja)
| { | ||
| "7be0c393d34092d8": "Acerca de", | ||
| "7f9e1070fd437434": [ | ||
| { | ||
| "c": [ | ||
| "Envuelve con <T>" | ||
| ], | ||
| "i": 1, | ||
| "t": "h2" | ||
| }, | ||
| { | ||
| "c": [ | ||
| "Envuelve cualquier contenido JSX en un componente <T> y se traducirá automáticamente." | ||
| ], | ||
| "i": 2, | ||
| "t": "p" | ||
| } | ||
| ], | ||
| "93505deebff4e9d6": [ | ||
| { | ||
| "c": "Renderizado del lado del servidor", | ||
| "i": 1, | ||
| "t": "h2" | ||
| }, | ||
| { | ||
| "c": "Las traducciones se cargan en el cargador raíz, por lo que el renderizado inicial ya aparece en el idioma correcto.", | ||
| "i": 2, | ||
| "t": "p" | ||
| } | ||
| ], | ||
| "985ef45524728a6f": "Documentación", | ||
| "c3bedf341bc197c1": [ | ||
| { | ||
| "c": "TanStack Start + General Translation", | ||
| "i": 1, | ||
| "t": "p" | ||
| }, | ||
| { | ||
| "c": "Internacionaliza tu aplicación de TanStack Start.", | ||
| "i": 2, | ||
| "t": "h1" | ||
| }, | ||
| { | ||
| "c": "Este ejemplo muestra gt-tanstack-start con traducciones locales. Usa el selector de idioma en la esquina superior derecha para cambiar de idioma.", | ||
| "i": 3, | ||
| "t": "p" | ||
| } | ||
| ], | ||
| "e64b93dc7849f179": [ | ||
| { |
There was a problem hiding this comment.
Translation files out of sync with source components
The content hashes in these translation files do not match the JSX structure currently in the source routes, so translations will silently fall back to English at runtime for several key components.
Mismatches found:
-
index.tsx– first<T>block (c3bedf341bc197c1): The JSON translation has ap → h1 → p(3-element) structure, but the current source hash1 → p(2 elements). The h1 text also differs: the translation was generated from"TanStack Start + General Translation", not the current"Welcome to TanStack Start + GT". -
about.tsx–<T>block (e64b93dc7849f179): Same structural mismatch — translation hasp → h1 → p(3 elements) but the component hash1 → p(2 elements). -
Orphaned translation keys present in the JSON but with no matching source content:
93505deebff4e9d6("SSR" card) — no corresponding<T>block exists in either route file.985ef45524728a6f("Documentation") — no matching content found in any route.7be0c393d34092d8("About") — the nav<a>About</a>in__root.tsxhas no<T>wrapper.
These files were likely generated from a previous iteration of the UI. Re-running npx gt translate against the current source files should regenerate the correct keys. Both es.json and ja.json are affected identically.
Prompt To Fix With AI
This is a comment left during a code review.
Path: examples/tanstack-start-basic/src/_gt/es.json
Line: 1-50
Comment:
**Translation files out of sync with source components**
The content hashes in these translation files do not match the JSX structure currently in the source routes, so translations will silently fall back to English at runtime for several key components.
Mismatches found:
1. **`index.tsx` – first `<T>` block** (`c3bedf341bc197c1`): The JSON translation has a `p → h1 → p` (3-element) structure, but the current source has `h1 → p` (2 elements). The h1 text also differs: the translation was generated from `"TanStack Start + General Translation"`, not the current `"Welcome to TanStack Start + GT"`.
2. **`about.tsx` – `<T>` block** (`e64b93dc7849f179`): Same structural mismatch — translation has `p → h1 → p` (3 elements) but the component has `h1 → p` (2 elements).
3. **Orphaned translation keys** present in the JSON but with no matching source content:
- `93505deebff4e9d6` ("SSR" card) — no corresponding `<T>` block exists in either route file.
- `985ef45524728a6f` ("Documentation") — no matching content found in any route.
- `7be0c393d34092d8` ("About") — the nav `<a>About</a>` in `__root.tsx` has no `<T>` wrapper.
These files were likely generated from a previous iteration of the UI. Re-running `npx gt translate` against the current source files should regenerate the correct keys. Both `es.json` and `ja.json` are affected identically.
How can I resolve this? If you propose a fix, please make it concise.| export default async function loadTranslations(locale: string) { | ||
| const translations = await import(`./src/_gt/${locale}.json`); | ||
| return translations.default; |
There was a problem hiding this comment.
Unsanitized locale used in dynamic import path
The locale string from the caller is interpolated directly into the dynamic import path with no validation. In an SSR context (which TanStack Start uses), a crafted locale value such as ../../../vite.config could potentially cause unintended module resolution. Since this function is called server-side in the root loader, the attack surface is real.
Consider validating the locale against a known allowlist before using it in the import path:
| export default async function loadTranslations(locale: string) { | |
| const translations = await import(`./src/_gt/${locale}.json`); | |
| return translations.default; | |
| export default async function loadTranslations(locale: string) { | |
| const allowed = ['es', 'ja'] // keep in sync with gt.config.json locales | |
| if (!allowed.includes(locale)) return {} | |
| const translations = await import(`./src/_gt/${locale}.json`) | |
| return translations.default | |
| } |
Alternatively, use a static mapping to eliminate the dynamic path entirely.
Prompt To Fix With AI
This is a comment left during a code review.
Path: examples/tanstack-start-basic/loadTranslations.ts
Line: 1-3
Comment:
**Unsanitized locale used in dynamic import path**
The `locale` string from the caller is interpolated directly into the dynamic import path with no validation. In an SSR context (which TanStack Start uses), a crafted locale value such as `../../../vite.config` could potentially cause unintended module resolution. Since this function is called server-side in the root loader, the attack surface is real.
Consider validating the locale against a known allowlist before using it in the import path:
```suggestion
export default async function loadTranslations(locale: string) {
const allowed = ['es', 'ja'] // keep in sync with gt.config.json locales
if (!allowed.includes(locale)) return {}
const translations = await import(`./src/_gt/${locale}.json`)
return translations.default
}
```
Alternatively, use a static mapping to eliminate the dynamic path entirely.
How can I resolve this? If you propose a fix, please make it concise.| locale: getLocale(), | ||
| } | ||
| }, | ||
| shellComponent: RootDocument, |
There was a problem hiding this comment.
React namespace used but not imported
React.ReactNode is referenced in the type annotation, but React is never imported. With "jsx": "react-jsx" the JSX transform is automatic, but the React namespace is still needed for the type reference. With "verbatimModuleSyntax": true and "strict": true in tsconfig.json, this will produce a TypeScript error: Cannot find name 'React'.
| shellComponent: RootDocument, | |
| function RootDocument({ children }: { children: import('react').ReactNode }) { |
Or add a type-only import at the top of the file:
import type { ReactNode } from 'react'and change the signature to { children: ReactNode }.
Prompt To Fix With AI
This is a comment left during a code review.
Path: examples/tanstack-start-basic/src/routes/__root.tsx
Line: 34
Comment:
**`React` namespace used but not imported**
`React.ReactNode` is referenced in the type annotation, but `React` is never imported. With `"jsx": "react-jsx"` the JSX transform is automatic, but the `React` namespace is still needed for the type reference. With `"verbatimModuleSyntax": true` and `"strict": true` in `tsconfig.json`, this will produce a TypeScript error: `Cannot find name 'React'`.
```suggestion
function RootDocument({ children }: { children: import('react').ReactNode }) {
```
Or add a type-only import at the top of the file:
```ts
import type { ReactNode } from 'react'
```
and change the signature to `{ children: ReactNode }`.
How can I resolve this? If you propose a fix, please make it concise.| "preview": "vite preview" | ||
| }, | ||
| "dependencies": { | ||
| "@tailwindcss/vite": "^4.1.18", | ||
| "@tanstack/react-router": "latest", | ||
| "@tanstack/react-start": "latest", | ||
| "@tanstack/router-plugin": "^1.132.0", | ||
| "gt-tanstack-start": "workspace:*", | ||
| "gt-react": "workspace:*", |
There was a problem hiding this comment.
Unpinned latest tags and build tool in production dependencies
Two concerns here:
-
@tanstack/react-routerand@tanstack/react-startare pinned tolatest. For an example app that is expected to remain runnable over time,latestcan silently break as new major versions ship. Pinning to a specific version (e.g.,^1.132.0) is more reliable. -
@tanstack/router-pluginis a build-time Vite/Rollup plugin and should live indevDependencies, notdependencies.
| "preview": "vite preview" | |
| }, | |
| "dependencies": { | |
| "@tailwindcss/vite": "^4.1.18", | |
| "@tanstack/react-router": "latest", | |
| "@tanstack/react-start": "latest", | |
| "@tanstack/router-plugin": "^1.132.0", | |
| "gt-tanstack-start": "workspace:*", | |
| "gt-react": "workspace:*", | |
| "@tanstack/react-router": "^1.132.0", | |
| "@tanstack/react-start": "^1.132.0", |
And move @tanstack/router-plugin to devDependencies.
Prompt To Fix With AI
This is a comment left during a code review.
Path: examples/tanstack-start-basic/package.json
Line: 9-17
Comment:
**Unpinned `latest` tags and build tool in production dependencies**
Two concerns here:
1. `@tanstack/react-router` and `@tanstack/react-start` are pinned to `latest`. For an example app that is expected to remain runnable over time, `latest` can silently break as new major versions ship. Pinning to a specific version (e.g., `^1.132.0`) is more reliable.
2. `@tanstack/router-plugin` is a build-time Vite/Rollup plugin and should live in `devDependencies`, not `dependencies`.
```suggestion
"@tanstack/react-router": "^1.132.0",
"@tanstack/react-start": "^1.132.0",
```
And move `@tanstack/router-plugin` to `devDependencies`.
How can I resolve this? If you propose a fix, please make it concise.
Adds a companion example app for the gt-tanstack-start docs.
What it demonstrates
initializeGT()+GTProvidersetup in the root routegetTranslations()in root loader for SSR<T>components for automatic JSX translation<LocaleSelector>for language switchingKey notes
<T>must be imported fromgt-react(notgt-tanstack-start) for the CLI to detect itsrc/_gt/(insidesrc/for Vite dynamic imports)Companion to the docs PR in generaltranslation/content.
Greptile Summary
This PR adds a new
examples/tanstack-start-basicexample app demonstrating how to integrategt-tanstack-startwith TanStack Start for SSR-friendly i18n. The overall structure —initializeGT()at module scope,getTranslations()in the root loader, and<GTProvider>wrapping the shell — is correct and follows best practices.However, there are four critical issues that prevent merging:
Stale translation files (
es.json/ja.json): Content hashes don't match the current JSX structure inindex.tsxandabout.tsx. The hero block and about-page translations have structural mismatches (3 elements in JSON vs. 2 in source), and several keys are orphaned with no corresponding source components. Re-runningnpx gt translateagainst the current source files is required.Path traversal in
loadTranslations.ts: Thelocaleparameter is interpolated directly into the dynamic import path with no validation. In SSR context, a crafted locale value could trigger unintended module resolution.Missing
Reactimport in__root.tsx:React.ReactNodeis referenced without importingReact. WithverbatimModuleSyntax: trueandstrict: true, this is a TypeScript compilation error.package.jsonconcerns:@tanstack/react-routerand@tanstack/react-startuselatestinstead of pinned versions, making the example fragile.@tanstack/router-pluginshould be indevDependencies, notdependencies.Confidence Score: 2/5
loadTranslations.tsis a security concern in an SSR context. The package.json issues (unpinned versions, misplaced dependency) are quality concerns that should be fixed before merging an example app that serves as a reference implementation.src/_gt/es.json,src/_gt/ja.json(stale keys),loadTranslations.ts(path traversal),src/routes/__root.tsx(missing React import),package.json(version pinning and dependency placement).Last reviewed commit: f4fa031