> [Agent navigation index](/llms.txt)

# Author fragments

Pair an ordinary Preact component with an auto-discovered HTML endpoint sidecar and model the finite inputs that can cross its transport boundary.

Use fragments for body content such as shared chrome, banners, or booking widgets whose cache lifetime
differs from the page shell. Do not use them for head metadata or repeated list rows. Fragment
transport is HTML-only; a page's semantic Markdown callback owns all non-HTML semantics.

## Generate the component and sidecar

```sh
brix add fragment siteHeader --feature chrome

```

The sidecar filename ends in `.fragment.ts`, lives under `src/features`, and exports exactly one
PascalCase runtime value ending in `Fragment`:

```tsx
// SiteHeader.tsx
export interface SiteHeaderProps {
  transparent: boolean;
}

export function SiteHeader({ transparent }: SiteHeaderProps) {
  return <header data-transparent={transparent}>Shared header</header>;
}

```

```ts
// siteHeader.fragment.ts
import { createFragment } from "#brix/server";
import { z } from "brix/zod";
import { SiteHeader } from "./SiteHeader.tsx";

export const SiteHeaderFragment = createFragment({
  name: "site-header",
  component: SiteHeader,
  endpoint: {
    params: z.object({
      transparent: z.enum(["true", "false"]).transform((value) => value === "true"),
    }),
  },
  cache: "sharedChrome",
});

```

The route is registered by exact component identity. Do not wrap or copy the component in the
sidecar, and do not register one component twice. Page JSX imports only `SiteHeader`; the fragment
export is a route handle for discovery and tests.

Render a fragment name at most once in a composed page. Duplicate names are rejected because scoped
hydration state is unique. A deferred fragment also cannot sit beneath a hydrated island: move it
outside that island or pass it through a server-rendered slot. See [islands](/docs/islands).

## Model endpoint inputs

`endpoint.params` selects component props to serialize into the fragment URL.
`endpoint.forwardPageQuery` allowlists values copied from the live outer page URL. Both must be finite
`z.object` schemas with URL-serializable scalar or array outputs, their keys cannot overlap, and every
params key must exist in component props. Unknown endpoint query keys return 400.

Use `load` for request-only values or to combine forwarded page query into complete props:

```ts
// BookingWidget and BOOKING_API_URL are application-specific.
export const BookingWidgetFragment = createFragment({
  name: "booking-widget",
  component: BookingWidget,
  endpoint: {
    params: z.object({ hotelId: z.string() }),
    forwardPageQuery: z.object({ campaign: z.string().optional() }),
    load: async (c, { params, pageQuery }) => ({
      hotelId: params.hotelId,
      campaign: pageQuery.campaign,
      bookingApiUrl: c.env.BOOKING_API_URL,
    }),
  },
  cache: "bookingWidget",
});

```

`load` returns all component props; Brix does not merge partial endpoint data. Inline SSR does not
call it because the page already supplied complete props. Without `load`, parsed params must be the
complete endpoint props.

Endpoint `auth`, `middleware`, `enabled`, and `cache` behave like page-route options. Configure server
infrastructure as described in [server setup](/docs/server).

Continue with [compose and test fragments](/docs/fragments-composition).
