Skip to content

feat: add TanStack Start example app with gt-tanstack-start#1080

Open
moss-bryophyta wants to merge 1 commit intogeneraltranslation:mainfrom
moss-bryophyta:moss/tanstack-start-example
Open

feat: add TanStack Start example app with gt-tanstack-start#1080
moss-bryophyta wants to merge 1 commit intogeneraltranslation:mainfrom
moss-bryophyta:moss/tanstack-start-example

Conversation

@moss-bryophyta
Copy link
Copy Markdown
Contributor

@moss-bryophyta moss-bryophyta commented Mar 6, 2026

Adds a companion example app for the gt-tanstack-start docs.

What it demonstrates

  • initializeGT() + GTProvider setup in the root route
  • getTranslations() in root loader for SSR
  • <T> components for automatic JSX translation
  • <LocaleSelector> for language switching
  • Local translation files (Spanish + Japanese)

Key notes

  • <T> must be imported from gt-react (not gt-tanstack-start) for the CLI to detect it
  • Translation files in src/_gt/ (inside src/ for Vite dynamic imports)

Companion to the docs PR in generaltranslation/content.

Greptile Summary

This PR adds a new examples/tanstack-start-basic example app demonstrating how to integrate gt-tanstack-start with 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:

  1. Stale translation files (es.json / ja.json): Content hashes don't match the current JSX structure in index.tsx and about.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-running npx gt translate against the current source files is required.

  2. Path traversal in loadTranslations.ts: The locale parameter is interpolated directly into the dynamic import path with no validation. In SSR context, a crafted locale value could trigger unintended module resolution.

  3. Missing React import in __root.tsx: React.ReactNode is referenced without importing React. With verbatimModuleSyntax: true and strict: true, this is a TypeScript compilation error.

  4. package.json concerns: @tanstack/react-router and @tanstack/react-start use latest instead of pinned versions, making the example fragile. @tanstack/router-plugin should be in devDependencies, not dependencies.

Confidence Score: 2/5

  • Not ready to merge — the example app won't demonstrate working translations due to stale translation keys, and there is a path-traversal risk in the SSR loader.
  • The stale translation files are a blocking functional bug for an app whose purpose is to demonstrate translations working. The missing React import is a TypeScript compilation error. The path traversal issue in loadTranslations.ts is 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

Greptile also left 4 inline comments on this PR.

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)
Comment on lines +1 to +50
{
"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": [
{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

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.

Comment on lines +1 to +3
export default async function loadTranslations(locale: string) {
const translations = await import(`./src/_gt/${locale}.json`);
return translations.default;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
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,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'.

Suggested change
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.

Comment on lines +9 to +17
"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:*",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
"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.

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

Successfully merging this pull request may close these issues.

1 participant