--- url: /docs/development.md --- # Developer Info {#development} This page briefly summarizes the development workflow for the `tanstack-head-controller` monorepo. ## Prerequisites * Node.js 22+ * pnpm 10.x ## Setup ::: code-group ```sh [ni] ni ``` ```sh [npm] npm install ``` ```sh [pnpm] pnpm install ``` ::: ## Build Run the following from the repository root. ::: code-group ```sh [ni] nr build ``` ```sh [npm] npm build ``` ```sh [pnpm] pnpm build ``` ::: Internally, `scripts/build.ts` does the following for each package. * Generates JavaScript with SWC * Generates declaration files (`d.ts`) with TypeScript To build individual packages: ::: code-group ```sh [node] node ./scripts/build.ts tanstack-head-controller node ./scripts/build.ts thc-plugin-merge ``` ::: ## Main packages * `packages/tanstack-head-controller`: core library * `packages/thc-plugin-merge`: head merge plugin * `packages/thc-plugin-ttplate`: title template plugin * `examples/tanstack-react`: integration example ## Docs update checklist * If you update Japanese pages, keep English pages in the same structure. * Keep code block language/file labels aligned (for example, `tsx [router.tsx]`). * Verify that all referenced links resolve correctly. When needed, also see [Quick Start](./getting-started) and [Usage](./usage). --- --- url: /docs/getting-started.md --- # Quick Start {#getting-started} This page shows the shortest path to integrate TanStack Head Controller into a TanStack Router app. ## 1. Install packages ::: code-group ```sh [ni] ni tanstack-head-controller thc-plugin-merge thc-plugin-ttplate ``` ```sh [npm] npm install tanstack-head-controller thc-plugin-merge thc-plugin-ttplate ``` ```sh [pnpm] pnpm add tanstack-head-controller thc-plugin-merge thc-plugin-ttplate ``` ::: ## 2. Create a controller and place it in router context ```tsx [router.tsx] import { createRouter } from '@tanstack/react-router' import { createHeadController } from 'tanstack-head-controller' import { thcMerge } from 'thc-plugin-merge' import { thcTitleTemplate } from 'thc-plugin-ttplate' export const router = createRouter({ routeTree, context: { ...createHeadController({ plugins: [ thcMerge(), thcTitleTemplate({ siteName: "My App" }) ] }) }, }) ``` ## 3. Render in your root document head ```tsx [__root.tsx] import { HeadControllerRender } from 'tanstack-head-controller' function RootDocument({ children }: { children: React.ReactNode }) { return ( {children} ) } ``` ## 4. Define head at route level ```tsx [about.tsx] export const Route = createFileRoute('/about')({ head: () => ({ meta: [ { title: 'About' }, { property: 'og:title', content: 'About' }, ], }), }) ``` ## 5. Optionally edit route context This example uses `editContext`, but the key idea is simple: keep existing route context intact and add only the settings you need. ```tsx [about.tsx] import { editContext } from 'tanstack-head-controller/context' export const Route = createFileRoute('/about')({ context: (ctx) => editContext(ctx, { configs: { debug: true }, }), }) ``` Next, see [Usage](./usage) for plugin patterns and practical tips. --- --- url: /contributors.md --- --- --- url: /docs/usage.md --- # Usage {#usage} This page covers practical patterns for using TanStack Head Controller in real apps. ## Baseline integration 1. Place the controller in router context (for example, `headCtrlr`) 2. Return head from routes 3. Place the render component (`HeadControllerRender`) in the root document head ## Combine plugins ```tsx import { createHeadController } from 'tanstack-head-controller' import { thcMerge } from 'thc-plugin-merge' import { thcTitleTemplate } from 'thc-plugin-ttplate' export const thc = createHeadController({ plugins: [ thcMerge(), thcTitleTemplate({ siteName: 'TanStack Starter', separator: ' | ', }), ], }) ``` ## Edit route context Use the helper (`editContext`) to merge controller-specific values while preserving existing route context. ```tsx [about.tsx] import { editContext } from 'tanstack-head-controller/context' export const Route = createFileRoute('/about')({ context: (ctx) => editContext(ctx, { configs: { debug: true, }, }), }) ``` ## Write a custom plugin ```tsx import { createThcPlugin } from 'tanstack-head-controller/plugins' export const forceNoIndex = () => createThcPlugin({ name: 'app.force-noindex', transform(head) { return { ...head, meta: [...(head.meta ?? []), { name: 'robots', content: 'noindex' }], } }, }) ``` ## Ordering guidance * Plugins run in registration order * Put normalization plugins first and final formatting plugins later ## Common production patterns * Define base metadata at layout level and override it at page level * Standardize OG tags with one plugin * Centralize environment-based robots behavior with a plugin Continue with [Quick Start](./getting-started) and [Developer Info](./development). --- --- url: /docs/what-is-thc.md --- # What is TanStack Head Controller? {#what-is-thc} TanStack Head Controller is a small library that collects head data from TanStack Router route matches, transforms that data through plugins, and renders the final tags in your document head. ::: tip If you are starting from scratch, go to the [Quick Start](./getting-started). ::: ## What it does * Collects route-level page data (meta, links, styles, and scripts) * Applies plugin transforms in sequence * Renders the resolved head with a React component ## Core API * Create a controller with configs and plugins (`createHeadController`) * Resolve and render head tags for the current route (`HeadControllerRender`) * Safely merge controller-related values into route context (`editContext`) ## Plugin model Plugins implement a transform function: * Input: current head payload and controller context * Output: transformed head payload Built-in examples in this monorepo: * Merge duplicate-like metadata entries (`thc-plugin-merge`) ## Typical use cases * Keep head logic centralized across nested layouts and page routes * Standardize title patterns without repeating route-level logic * Add custom SEO transforms without modifying the core library ## Notes * Current implementation resolves head primarily from route matches * Script entries are collected from route script definitions (`headScripts`) Continue with [Quick Start](./getting-started) and [Usage](./usage).