diff --git a/src/routes/solid-meta/(0)index.mdx b/src/routes/solid-meta/(0)index.mdx index d22bdef8f6..5c07a31d57 100644 --- a/src/routes/solid-meta/(0)index.mdx +++ b/src/routes/solid-meta/(0)index.mdx @@ -17,6 +17,10 @@ description: >- meta tags at any component level for flexible SEO and metadata control. --- +:::note[Using Solid 2.0?] +This documentation covers `@solidjs/meta` 0.29.x for Solid 1.x. For Solid 2.x, use `@solidjs/meta` 1.x and the [v1 documentation](/solid-meta/v1). +::: + Solid Meta offers asynchronous SSR-ready Document Head management for Solid Applications, based on [React Head](https://github.com/tizmagik/react-head) With Solid Meta, you can define `document.head` tags at any level of your component hierarchy. diff --git a/src/routes/solid-meta/v1/(0)index.mdx b/src/routes/solid-meta/v1/(0)index.mdx new file mode 100644 index 0000000000..4af8ee04fd --- /dev/null +++ b/src/routes/solid-meta/v1/(0)index.mdx @@ -0,0 +1,34 @@ +--- +title: Overview +titleTemplate: ":title" +mainNavExclude: true +use_cases: >- + managing head tags, seo optimization, document metadata, dynamic meta tags, + ssr meta management, streaming head updates +tags: + - meta + - head + - seo + - ssr + - overview +version: "1.0" +description: >- + Solid Meta 1.0 provides document head management for Solid 2.0 — declare + head tags anywhere with streaming-correct SSR and flicker-free hydration. +--- + +:::note[Solid Meta 1.0 is for Solid 2.0] +Solid Meta 1.x requires Solid 2.x (currently in beta). If you are using Solid 1.x, use `@solidjs/meta` 0.29.x and the [latest documentation](/solid-meta) instead. +::: + +Solid Meta provides document head management for Solid applications — declare ``, `<meta>`, `<link>`, and other head elements anywhere in your component tree, with streaming-correct server rendering and flicker-free hydration. + +Solid Meta 1.x is a thin component layer over Solid 2.0's built-in head registry (`useHead` in `@solidjs/web`). There is **no provider** — the registry is ambient. Render a head component anywhere and it registers under the current reactive owner, and server rendering splices the winning tags into your document automatically. + +| Solid version | @solidjs/meta version | +| ------------- | --------------------- | +| Solid 2.x | 1.x | +| Solid 1.x | 0.27.x – 0.29.x | +| Solid 0.x | 0.26.x | + +If you are upgrading an existing app from `@solidjs/meta` 0.x, start with the [migration guide](/solid-meta/v1/migrating-from-v0). diff --git a/src/routes/solid-meta/v1/(1)getting-started.mdx b/src/routes/solid-meta/v1/(1)getting-started.mdx new file mode 100644 index 0000000000..0df1eb0334 --- /dev/null +++ b/src/routes/solid-meta/v1/(1)getting-started.mdx @@ -0,0 +1,84 @@ +--- +title: Getting started +use_cases: >- + installation, setup, first head tags, page titles, meta tags, ssr setup +tags: + - setup + - installation + - quickstart + - head + - meta +version: "1.0" +description: >- + Install Solid Meta 1.0 and manage document head tags from anywhere in your + Solid 2.0 application. +--- + +## Installation + +```package-install +@solidjs/meta +``` + +Solid Meta 1.x requires `solid-js` and `@solidjs/web` 2.0 (`2.0.0-beta.31` or later). + +## Usage + +There is no provider and no other setup. +Render head components anywhere in your component tree: + +```tsx +import { Title, Link, Meta } from "@solidjs/meta"; + +function Home() { + return ( + <div class="Home"> + <Title>Title of page + + + + ); +} +``` + +Head tags follow a few consistent rules: + +- **Later wins.** + Tags deduplicate by identity (each [component's reference page](/solid-meta/v1/reference/meta/title) documents its identity rule); the last-registered tag for an identity is the one in the document. +- **Disposal restores.** + When the winning tag's component unmounts, the previous registration for that identity is restored — navigating away from a page undoes its head changes automatically. +- **Reactive.** + Attribute values and text children can be reactive expressions; updates apply in place without losing the tag's position in the override order. + +Every component accepts a `key` prop that overrides the default identity — use it to make otherwise-distinct tags override each other, or to fork an identity that would otherwise collide: + +```tsx +{/* These override each other despite different attributes: */} + + +``` + +To manage a _set_ of tags as one unit — several `og:image`s, a block of social tags that should override together — wrap them in [``](/solid-meta/v1/reference/meta/head). + +## Server rendering + +Server rendering requires no wiring. +Render your document with Solid and the head manages itself: + +```tsx +import { renderToStream } from "@solidjs/web"; +import App from "./App"; + +// ... within the context of a request ... +renderToStream(() => ).pipe(res); +``` + +Winning tags are spliced into `` on the first flush (`` and `` go right after `` opens; resource links go early). +Tags registered under a suspense boundary that completes later stream to the client as patches that apply when the boundary reveals. +If you assemble the document yourself, use the `onHead` render option to receive the head markup instead. + +On the client, hydration adopts server-rendered head tags in place — there is no removal/re-insertion flicker. + +:::note[Static shell tags] +A static `` in your server shell acts as the fallback when no `<Title>` is mounted. Don't hardcode other tags that Solid Meta should manage — the registry leaves foreign head tags alone, so a hardcoded `<meta name="description">` would coexist with a rendered one. +::: diff --git a/src/routes/solid-meta/v1/(2)migrating-from-v0.mdx b/src/routes/solid-meta/v1/(2)migrating-from-v0.mdx new file mode 100644 index 0000000000..b03a7fcf74 --- /dev/null +++ b/src/routes/solid-meta/v1/(2)migrating-from-v0.mdx @@ -0,0 +1,82 @@ +--- +title: Migrating from 0.x +use_cases: >- + existing project, migration, upgrade, metaprovider removal +tags: + - migration + - upgrade + - metaprovider + - breaking-changes +version: "1.0" +description: >- + Migrate your application from @solidjs/meta 0.x to 1.0. +--- + +Solid Meta 1.0 was rebuilt as a thin layer over Solid 2.0's built-in head registry. +Most components keep their names and props, but the provider, the server plumbing, and the deduplication semantics changed. + +## Migration steps + +### Delete `<MetaProvider>` + +The head registry is ambient — the provider and `MetaContext` no longer exist. +Remove the wrapper: + +```tsx del={1,5,7} +import { MetaProvider } from "@solidjs/meta"; + +export default function App() { + return ( + <MetaProvider> + <Layout /> + </MetaProvider> + ); +} +``` + +### Delete server plumbing + +The 0.x server flow — passing a `tags={[]}` array into `MetaProvider` and splicing `renderTags(tags)` into your template — is gone. +Rendering the document with `renderToString` / `renderToStream` splices the winning tags into `<head>` automatically, and tags registered under suspense boundaries stream to the client as patches. +If you assemble the HTML document yourself, use the `onHead` render option to receive the head markup instead. + +### Review duplicate-tag semantics + +0.x kept multiple `<Meta>` tags with the same `name` if their other attributes differed. +1.x dedupes by `name`/`property`/`http-equiv` (qualified by `media`) with last-wins. + +For deliberate sets — multiple `og:image`s that should coexist — wrap them in [`<Head>`](/solid-meta/v1/reference/meta/head): + +```tsx +<Head> + <Meta property="og:image" content="/image-1.png" /> + <Meta property="og:image" content="/image-2.png" /> +</Head> +``` + +To fork an identity that would otherwise collide, give each tag a distinct `key`. + +### Update `useHead` calls + +`useHead` is no longer exported from `@solidjs/meta` — the primitive belongs to Solid 2.0 itself. +Import it from `@solidjs/web`; it takes `HeadTag` descriptors (`{ tag, props, key? }`) — a single tag, an array (a group), or a function (a reactive group): + +```tsx +import { useHead } from "@solidjs/web"; + +useHead({ tag: "meta", props: { name: "description", content: () => desc() } }); +``` + +### Removed features + +- **`escape` prop** — everything is escaped now; text is applied via `textContent`, so markup injection isn't possible. +- **`ref` and event handlers on head tags** — head tags are data, not managed elements. Query the DOM directly for the rare case that needs it. +- **Client-dynamic `<Base>` / `<Meta charset>`** — these are rendered into the server shell only, and are ignored (with a dev warning) on the client. A base or charset that changes after the document loaded is incoherent. +- **`noscript`** — excluded from the core tag union: author it statically in your document shell. + +### New capabilities + +- [`<Script>`](/solid-meta/v1/reference/meta/script) is new — JSON-LD and other head scripts no longer need the `useHead` escape hatch. +- [`<Head>`](/solid-meta/v1/reference/meta/head) groups child tags into one replacement set with reactive membership. +- Icons (`rel="icon"` / `rel="apple-touch-icon"`) are replaceable: swapping the `href` replaces the favicon rather than accumulating, and unmounting restores the previous one. +- `theme-color` variants with different `media` queries coexist. diff --git a/src/routes/solid-meta/v1/reference/meta/base.mdx b/src/routes/solid-meta/v1/reference/meta/base.mdx new file mode 100644 index 0000000000..fdef96f0dd --- /dev/null +++ b/src/routes/solid-meta/v1/reference/meta/base.mdx @@ -0,0 +1,56 @@ +--- +title: Base +order: 7 +use_cases: >- + base url, relative url resolution, document base +tags: + - base + - head + - url + - component +version: "1.0" +description: >- + Base sets the document base URL through Solid Meta during server rendering. +--- + +`Base` adds a [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) element that specifies the base URL for all relative URLs in the document. + +## Import + +```tsx +import { Base } from "@solidjs/meta"; +``` + +## Type + +```tsx +const Base: Component<JSX.BaseHTMLAttributes<HTMLBaseElement>>; +``` + +## Props + +Accepts attributes for [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) (`href`, `target`). + +## Behavior + +- **Server shell only.** + `<base>` has hard placement constraints — it must appear before any relative URL resolves — so it is rendered into the head prelude on the first server flush. +- Registrations arriving after the shell has flushed are ignored with a dev warning, and `Base` is ignored (with a dev warning) on the client: a base that changes after relative URLs resolved is incoherent by definition. +- `Base` does not accept a `key` and does not participate in cascade/restore semantics. + +## Examples + +### Basic usage + +```tsx +import { Base } from "@solidjs/meta"; + +export default function App() { + return <Base href="https://example.com/app/" target="_blank" />; +} +``` + +## Related + +- [`Link`](/solid-meta/v1/reference/meta/link) +- [`Meta`](/solid-meta/v1/reference/meta/meta) diff --git a/src/routes/solid-meta/v1/reference/meta/head.mdx b/src/routes/solid-meta/v1/reference/meta/head.mdx new file mode 100644 index 0000000000..a77858af7f --- /dev/null +++ b/src/routes/solid-meta/v1/reference/meta/head.mdx @@ -0,0 +1,96 @@ +--- +title: Head +order: 8 +use_cases: >- + grouped meta tags, multiple og images, social tag blocks, atomic head + replacement +tags: + - head + - group + - og-tags + - component +version: "1.0" +description: >- + Head groups its child head tags into one replacement set through Solid Meta. +--- + +`Head` groups its child head tags into one replacement set. +Some head state is a _set_, not a single tag — multiple `og:image`s, or a block of social tags that should override together. + +:::note[New in 1.0] +`Head` did not exist in `@solidjs/meta` 0.x. +::: + +## Import + +```tsx +import { Head } from "@solidjs/meta"; +``` + +## Type + +```tsx +const Head: ParentComponent; +``` + +## Props + +### `children` + +- **Type:** `JSX.Element` +- **Optional:** Yes + +Head tag components (and any components that render them). + +## Behavior + +- **Within the group, same-identity tags coexist.** + Two `<Meta property="og:image">` tags inside one `<Head>` both render. +- **Groups replace wholesale.** + A later group replaces an earlier group's set for an identity as a unit, and unmounting restores the earlier set. +- **Membership is reactive.** + Tags rendered conditionally (or by child components) inside a `<Head>` join and leave the set as they mount and unmount. + Group scope propagates via context through component calls. +- **Nesting starts a new group.** + A `<Head>` inside another `<Head>`'s children forms its own independent group; to contribute tags _into_ the surrounding group, render bare tag components instead. + +## Examples + +### Overriding a default set + +```tsx +// Layout +<Head> + <Meta property="og:image" content="/default-1.png" /> + <Meta property="og:image" content="/default-2.png" /> +</Head> + +// Page — replaces BOTH defaults while mounted, restores them on leave +<Head> + <Meta property="og:image" content={product().image} /> +</Head> +``` + +### Social tag block + +```tsx +import { Head, Meta, Title } from "@solidjs/meta"; + +export default function Article(props: { + article: () => { title: string; image: string }; +}) { + return ( + <Head> + <Title>{props.article().title} + + + + + ); +} +``` + +## Related + +- [`Meta`](/solid-meta/v1/reference/meta/meta) +- [`Title`](/solid-meta/v1/reference/meta/title) diff --git a/src/routes/solid-meta/v1/reference/meta/link.mdx b/src/routes/solid-meta/v1/reference/meta/link.mdx new file mode 100644 index 0000000000..d963152c92 --- /dev/null +++ b/src/routes/solid-meta/v1/reference/meta/link.mdx @@ -0,0 +1,85 @@ +--- +title: Link +order: 3 +use_cases: >- + canonical links, favicons, preload hints, stylesheets, alternate links +tags: + - link + - head + - favicon + - preload + - component +version: "1.0" +description: >- + Link adds a link element to the document head through Solid Meta. +--- + +`Link` adds a [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) element to the document head. + +## Import + +```tsx +import { Link } from "@solidjs/meta"; +``` + +## Type + +```tsx +const Link: Component< + JSX.LinkHTMLAttributes & { key?: string } +>; +``` + +## Props + +Accepts attributes for [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link). +Attribute values can be reactive expressions. + +### `key` + +- **Type:** `string` +- **Optional:** Yes + +Overrides the default identity used for deduplication. + +## Behavior + +- Dedupes by `rel` + `href`: the last-registered tag wins and unmounting restores the previous one. +- **Icons** (`rel="icon"`, `rel="apple-touch-icon"`) dedupe by `rel` + `sizes` + `type` instead — deliberately excluding `href`. + Swapping the `href` _replaces_ the favicon rather than accumulating, while size and type variants coexist. +- **Resource rels** (`preload`, `modulepreload`, `prefetch`, `preconnect`, `dns-prefetch`) render immediately and are never retracted — a fetch hint cannot be meaningfully undone. +- **Stylesheets** (`rel="stylesheet"`) are emitted eagerly (SSR streams them as soon as they register) and removed when their owner disposes. + +## Examples + +### Canonical link + +```tsx +import { Link } from "@solidjs/meta"; + +export default function Page() { + return ; +} +``` + +### Per-route favicon + +Because icon identity excludes `href`, this replaces the site favicon while the route is mounted and restores the previous one on leave: + +```tsx +import { Link } from "@solidjs/meta"; + +export default function Inbox(props: { unread: () => number }) { + return ( + 0 ? "/favicon-badge.ico" : "/favicon.ico"} + /> + ); +} +``` + +## Related + +- [`Stylesheet`](/solid-meta/v1/reference/meta/stylesheet) +- [`Meta`](/solid-meta/v1/reference/meta/meta) diff --git a/src/routes/solid-meta/v1/reference/meta/meta.mdx b/src/routes/solid-meta/v1/reference/meta/meta.mdx new file mode 100644 index 0000000000..61106d5952 --- /dev/null +++ b/src/routes/solid-meta/v1/reference/meta/meta.mdx @@ -0,0 +1,100 @@ +--- +title: Meta +order: 2 +use_cases: >- + meta tags, seo metadata, open graph tags, social sharing, descriptions, + theme color +tags: + - meta + - head + - seo + - og-tags + - component +version: "1.0" +description: >- + Meta adds a meta element to the document head through Solid Meta. +--- + +`Meta` adds a [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) element to the document head. + +## Import + +```tsx +import { Meta } from "@solidjs/meta"; +``` + +## Type + +```tsx +const Meta: Component< + JSX.MetaHTMLAttributes & { key?: string } +>; +``` + +## Props + +Accepts attributes for [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta). +Attribute values can be reactive expressions. + +### `key` + +- **Type:** `string` +- **Optional:** Yes + +Overrides the default identity used for deduplication. + +## Behavior + +- Dedupes by `name`, `property`, or `http-equiv`. + These are separate namespaces: `` and `` coexist. +- A `media` attribute forks identity, so `theme-color` light and dark variants coexist: + +```tsx + + +``` + +- The last-registered tag for an identity wins; unmounting it restores the previous registration. +- A `` with none of the identity attributes (and no `key`) is append-only. +- `` is server-shell only: it is rendered into the head prelude on the first flush and ignored (with a dev warning) on the client. + +## Examples + +### Basic usage + +```tsx +import { Meta } from "@solidjs/meta"; + +export default function Page() { + return ( + <> + + + + ); +} +``` + +### Forcing tags to override each other + +```tsx +{/* These override each other despite different attributes: */} + + +``` + +### Multiple tags with the same identity + +Wrap deliberate sets in [``](/solid-meta/v1/reference/meta/head) so they coexist and override as a unit: + +```tsx + + + + +``` + +## Related + +- [`Head`](/solid-meta/v1/reference/meta/head) +- [`Title`](/solid-meta/v1/reference/meta/title) diff --git a/src/routes/solid-meta/v1/reference/meta/script.mdx b/src/routes/solid-meta/v1/reference/meta/script.mdx new file mode 100644 index 0000000000..c37dbb580c --- /dev/null +++ b/src/routes/solid-meta/v1/reference/meta/script.mdx @@ -0,0 +1,90 @@ +--- +title: Script +order: 6 +use_cases: >- + json-ld, structured data, analytics scripts, head scripts +tags: + - script + - json-ld + - structured-data + - head + - component +version: "1.0" +description: >- + Script adds a script element to the document head through Solid Meta. +--- + +`Script` adds a [` + ); +} +``` + +## Related + +- [`Style`](/solid-meta/v1/reference/meta/style) +- [`Head`](/solid-meta/v1/reference/meta/head) diff --git a/src/routes/solid-meta/v1/reference/meta/style.mdx b/src/routes/solid-meta/v1/reference/meta/style.mdx new file mode 100644 index 0000000000..9d00364bdb --- /dev/null +++ b/src/routes/solid-meta/v1/reference/meta/style.mdx @@ -0,0 +1,82 @@ +--- +title: Style +order: 5 +use_cases: >- + inline styles, critical css, css-in-js output +tags: + - style + - css + - head + - component +version: "1.0" +description: >- + Style adds an inline style element to the document head through Solid Meta. +--- + +`Style` adds a [`; +} +``` + +### Replaceable themed style + +```tsx +import { Style } from "@solidjs/meta"; + +export default function Theme(props: { accent: () => string }) { + return ; +} +``` + +## Related + +- [`Stylesheet`](/solid-meta/v1/reference/meta/stylesheet) +- [`Script`](/solid-meta/v1/reference/meta/script) diff --git a/src/routes/solid-meta/v1/reference/meta/stylesheet.mdx b/src/routes/solid-meta/v1/reference/meta/stylesheet.mdx new file mode 100644 index 0000000000..da35e3ff23 --- /dev/null +++ b/src/routes/solid-meta/v1/reference/meta/stylesheet.mdx @@ -0,0 +1,66 @@ +--- +title: Stylesheet +order: 4 +use_cases: >- + stylesheets, css loading, route-scoped styles +tags: + - stylesheet + - css + - link + - head + - component +version: "1.0" +description: >- + Stylesheet adds a stylesheet link element to the document head through + Solid Meta. +--- + +`Stylesheet` is sugar for [``](/solid-meta/v1/reference/meta/link). + +## Import + +```tsx +import { Stylesheet } from "@solidjs/meta"; +``` + +## Type + +```tsx +const Stylesheet: Component< + Omit, "rel"> & { key?: string } +>; +``` + +## Props + +Accepts attributes for [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) except `rel`, which is fixed to `stylesheet`. + +### `key` + +- **Type:** `string` +- **Optional:** Yes + +Overrides the default identity used for deduplication. + +## Behavior + +- Emitted eagerly: during SSR the stylesheet streams as soon as it registers, so styles load as early as possible. +- Removed when its owner disposes — navigating away from a route removes its route-scoped stylesheet. +- Dedupes by URL: registering the same `href` twice yields one element. + +## Examples + +### Route-scoped stylesheet + +```tsx +import { Stylesheet } from "@solidjs/meta"; + +export default function Dashboard() { + return ; +} +``` + +## Related + +- [`Link`](/solid-meta/v1/reference/meta/link) +- [`Style`](/solid-meta/v1/reference/meta/style) diff --git a/src/routes/solid-meta/v1/reference/meta/title.mdx b/src/routes/solid-meta/v1/reference/meta/title.mdx new file mode 100644 index 0000000000..71869a69d5 --- /dev/null +++ b/src/routes/solid-meta/v1/reference/meta/title.mdx @@ -0,0 +1,78 @@ +--- +title: Title +order: 1 +use_cases: >- + page titles, document titles, browser tab text, head metadata +tags: + - title + - head + - meta + - component +version: "1.0" +description: >- + Title sets the document title through Solid Meta. +--- + +`Title` adds a [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title) element that sets the document title. + +## Import + +```tsx +import { Title } from "@solidjs/meta"; +``` + +## Type + +```tsx +const Title: Component<JSX.HTMLAttributes<HTMLTitleElement> & { key?: string }>; +``` + +## Props + +### `children` + +- **Type:** `JSX.Element` +- **Optional:** Yes + +Text content of the `title` element. +Applied via `textContent`, so it is always escaped. +Can be a reactive expression. + +### `key` + +- **Type:** `string` +- **Optional:** Yes + +Overrides the default identity used for deduplication. + +## Behavior + +- `title` is a hard singleton: the last-registered `<Title>` wins regardless of attributes. +- Unmounting the winning `<Title>` restores the previous one; a static `<title>` in your server shell is the final fallback. + +## Examples + +### Basic usage + +```tsx +import { Title } from "@solidjs/meta"; + +export default function Page() { + return <Title>Solid Docs; +} +``` + +### Reactive title + +```tsx +import { Title } from "@solidjs/meta"; + +export default function Product(props: { name: () => string }) { + return {props.name()} | My Store; +} +``` + +## Related + +- [`Meta`](/solid-meta/v1/reference/meta/meta) +- [`Head`](/solid-meta/v1/reference/meta/head) diff --git a/vite.config.ts b/vite.config.ts index 21b2ae08d3..dc03bf5f6c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -61,6 +61,7 @@ export default defineConfig({ default: "latest", values: { latest: { path: "", label: "Latest" }, + v1: { path: "v1", label: "v1" }, v2: { path: "v2", label: "v2" }, }, }, @@ -74,9 +75,13 @@ export default defineConfig({ version: ["latest", "v2"], }, { - project: ["router", "meta"], + project: "router", version: "latest", }, + { + project: "meta", + version: ["latest", "v1"], + }, ], }, overrides: [ @@ -122,7 +127,24 @@ export default defineConfig({ title: "Solid Meta", themeConfig: { sidebar: { - "/solid-meta": createFilesystemSidebar("./src/routes/solid-meta"), + "/solid-meta": createFilesystemSidebar( + "./src/routes/solid-meta", + { + filter: (item) => !item.filePath.includes("/solid-meta/v1"), + } + ), + }, + }, + }, + { + project: "meta", + version: "v1", + title: "Solid Meta", + themeConfig: { + sidebar: { + "/solid-meta/v1": createFilesystemSidebar( + "./src/routes/solid-meta/v1" + ), }, }, },