Files
supabase/apps/studio/compat/next/head.tsx
Alaister Young 6946ec2b2d build(studio): Next-compat shims (stack 2/6, from #46424) (#47110)
**Stack 2/6** of the TanStack Start migration (#46424). Stacked on
**#47107** (S1) — review that first; this PR's diff is just the compat
shims.

> [!NOTE]
> Purely additive. Next never imports these files — under TanStack
they're wired in via Vite aliases (`next/*` → `@/compat/next/*`). No
routes consume them yet (that begins in stack 3).

## What's in this PR
`apps/studio/compat/next/*` — drop-in shims so the existing pages-router
code runs unchanged under TanStack Start:
- `link`, `router`, `navigation`, `head`, `image`, `legacy/image`,
`script`, `dynamic`, `server`, `_router-events` — React/runtime shims
over `@tanstack/react-router`.
- `api.ts` — `toWebHandler`, which adapts a pages-router API handler
`(req, res)` into a TanStack server-route Web `fetch` handler.

## Verification
On top of S1: `studio` typecheck ✓, lint (0 errors) ✓. Next build is
unaffected (nothing imports these under tsc).


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added broad Next.js compatibility support for routing, links, dynamic
imports, images, scripts, head metadata, navigation hooks, server
responses, and API handlers.
* Improved handling of redirects, pathname/search params, base paths,
and event callbacks for smoother app behavior.

* **Tests**
* Added coverage for URL resolution and dynamic route interpolation to
verify Next-style routing behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
2026-06-25 16:52:34 +08:00

32 lines
1.6 KiB
TypeScript

import type { ReactNode } from 'react'
// Next/Head's job is to inject children into the document `<head>` and
// deduplicate them by `key` prop. React 19 ships native "document
// metadata" hoisting: any `<title>`, `<meta>`, `<link>`, `<style>`, or
// `<script>` rendered anywhere in the tree is hoisted to `<head>`
// automatically and works for both client render and SSR. Studio uses
// `<Head>` exclusively for those elements (`<title>`, `<meta>`,
// `<link>` — see `pages/maintenance.tsx`, `pages/claim-project.tsx`,
// etc.), so the shim can be a passthrough that lets React do the
// hoisting.
//
// Trade-offs vs the previous `createPortal(children, document.head)`
// approach:
// - SSR: the portal returned `null` on the server, so head content
// from `<Head>` was never in the prerendered HTML. Native hoisting
// emits the metadata in the prerendered output.
// - Deduplication: React 19 dedupes `<title>` (last one wins, same
// as `document.title`) and merges `<meta>`/`<link>` by their
// attributes. Next dedupes by an explicit `key` prop. The
// observable output is equivalent for the consumer set we have.
// - Non-metadata children: React 19 only hoists the metadata tags
// listed above. If a consumer ever renders an arbitrary element
// inside `<Head>`, it'll render in place rather than in `<head>`.
// We have no such consumers today; if one shows up, swap to a
// portal+SSR-collector setup.
// eslint-disable-next-line no-restricted-exports
export default function Head({ children }: { children?: ReactNode }) {
return <>{children}</>
}