API Reference
The api-reference package renders an OpenAPI 3.x document as a browsable API reference — tag-grouped navigation, per-operation parameter and schema trees, generated cURL and JavaScript snippets, a copy-as-Markdown button, and a request playground that executes real requests from the browser. It pairs naturally with Spectacular, which generates OpenAPI documents from a Laravel application, but renders any valid document.
The component below is live — browse the operations, inspect schemas, and switch snippet languages.
Installation
Section titled “Installation”composer require lattice-php/api-referenceThe package ships its React renderer as source, and the lattice() Vite plugin compiles it
into your app’s bundle via virtual:lattice/plugins (see
Component packages). The PHP component is picked up by
Lattice’s discovery automatically.
The component styles itself with Tailwind utilities, so add the package’s stylesheet to your
Tailwind entry after the Lattice import — its @source directive makes your build scan the
package’s components:
@import "@lattice-php/lattice/css";@import "@lattice-php/api-reference/css";Pass a decoded OpenAPI document to the component on any page. With Spectacular installed, Scramble’s generator produces the document — cache it rather than regenerating per request:
use Dedoc\Scramble\Generator;use Illuminate\Support\Facades\Cache;use Lattice\ApiReference\ApiReference;use Lattice\Core\Attributes\AsPage;use Lattice\Http\Page;use Lattice\Ui\PageSchema;
#[AsPage(route: 'docs', name: 'docs', middleware: ['auth'])]final class ApiDocsPage extends Page{ public function render(PageSchema $schema, Generator $generator): PageSchema { $document = Cache::rememberForever( 'openapi.document', fn (): array => $generator(), );
return $schema->schema([ApiReference::make()->spec($document)]); }}Alternatively ->url('/openapi.json') makes the browser fetch the document from a URL instead
of embedding it in the page props.
Scoping and presentation
Section titled “Scoping and presentation”ApiReference::make() ->spec($document) ->tag(['Users', 'Roles']) // only these navigation groups ->defaultOperation('users.index') // initial selection ->title('Acme API') // overrides info.title ->hideHeader() // drop the title/version header ->hideBaseUrl() // drop the server picker ->expandDepth(3) // schema tree levels expanded by default ->twoColumnBreakpoint(Breakpoint::Xl);->operation('users.show') pins the reference to a single operation with no navigation — useful
for embedding one endpoint’s documentation inside another page.
The selected operation is mirrored to the URL hash, so operation links deep-link and survive reloads.
Tooltips
Section titled “Tooltips”Alongside description, which renders as prose, the reference reads an x-tooltip extension —
a short piece of HTML, typically a link to deeper documentation, revealed from an info button
next to the field. Write it as a sibling of description on a schema property, a parameter (at
parameter level, not inside its schema), or an operation:
{ "summary": "List users", "description": "Returns every user in the account.", "x-tooltip": "<a href=\"https://docs.example.com/users\">User guide</a>", "parameters": [ { "name": "filter[type]", "in": "query", "description": "Restricts the result set by type.", "x-tooltip": "<a href=\"https://docs.example.com/filtering\">Filtering guide</a>" } ]}Request playground
Section titled “Request playground”Every operation carries a playground: path, query, and header parameters render as typed inputs (enum parameters become selects), JSON request bodies get an editor pre-filled with an example derived from the schema, and Execute sends the request from the browser against the server selected in the picker — responses stream into a live panel with status and headers.
ApiReference::make() ->spec($document) ->token($apiToken);->token() pre-fills Authorization: Bearer for operations whose security scheme accepts a
bearer token. Requests are plain browser fetch calls, so the API must be same-origin or send
CORS headers for the reference’s origin.
Lazy scoped tokens
Section titled “Lazy scoped tokens”A static token exists for the whole page life and carries one fixed scope set. With a remote source the playground instead fetches a short-lived access token scoped to exactly the operation being executed, the first time Execute is pressed:
ApiReference::make('api-docs') ->id('api-docs') ->spec($document) ->tokenSource('api-docs-tokens', audience: $tenant->slug);->tokenSource() reads every operation’s bearer scope set out of the inline spec and seals a
signed remote token access per distinct set — the browser can only request exactly those scope
combinations, anything else fails the ref check. On execute, the playground resolves a token
through the source’s issueBrowserToken() (cached per scope set until shortly before expiry,
retried once on a 401), so no credential is minted just by opening the page. audience is
sealed alongside the scopes and is the natural place for a tenant identifier. When both are
configured, tokenSource() wins over token().
->tokenSource() is the Laravel side of this mechanism; hosts without a Lattice backend get
the same lazy-on-execute behaviour through the resolveAccessToken prop described under
Use without Laravel.
The renderer is published to npm as @lattice-php/api-reference, so any React frontend — an
Astro docs site, a Vite SPA — renders the same reference without a Lattice backend:
npm install @lattice-php/api-referenceImport the stylesheet into your Tailwind v4 entry. It pulls in the Lattice design tokens and
adds the @source directives that make your build scan the package’s compiled components:
@import "tailwindcss";@import "@lattice-php/api-reference/css";Render the component with a decoded OpenAPI document, or a url the browser fetches:
import { ApiReference } from "@lattice-php/api-reference";import spec from "./openapi.json";
export default function ApiDocs() { return <ApiReference spec={spec} />;}For lazy per-operation tokens without any backend, pass resolveAccessToken — the playground
calls it on Execute with the operation’s bearer scope set and puts the result into the
Authorization header:
<ApiReference spec={spec} resolveAccessToken={async ({ scopes, forceRefresh }) => { const response = await fetch("https://auth.example.com/playground-token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ scopes }), }); const { access_token, expires_in } = await response.json();
return { accessToken: access_token, expiresIn: expires_in }; }}/>Return expiresIn (seconds) to let the playground cache the token per scope set until shortly
before expiry; a plain string is never cached, so the host owns reuse. On a 401 the playground
calls the resolver once more with forceRefresh: true and retries the request. A rejected
promise’s message is shown next to Execute. resolveAccessToken wins over remoteTokens
and token.
Props mirror the PHP component — operation, tags, defaultOperation, title,
hideHeader, hideBaseUrl, expandDepth, twoColumnBreakpoint, token,
resolveAccessToken — plus two selection controls:
deepLinking(defaulttrue) mirrors the expanded operation tolocation.hash. Passfalsewhen the host page owns the hash.selectedOperation/onOperationChangeswitch the reference to controlled selection for hosts with their own routing.
Icons ship inside the package: the component provides its own sprite when no SpriteProvider
is mounted above it, and a host-provided sprite always wins.
In Astro
Section titled “In Astro”Mount the component as a React island. It renders on the server, so client:load prerenders
the reference into the built HTML:
---import ApiDocs from "../components/ApiDocs";---
<ApiDocs client:load />Bundle size
Section titled “Bundle size”The breakdown below measures the package’s built plugin — React and the framework runtime are external, so this is exactly what the API reference adds on top of an app that already ships Lattice. It is regenerated on every docs build.
24.0 KB gzipped · 94.2 KB raw JavaScript
| Dependency | Raw | Gzip | Share |
|---|---|---|---|
| @lattice-php/api-reference | 83.2 KB | 22.9 KB | 95.5% |
| Bundler runtime | 11.0 KB | 1.1 KB | 4.5% |
Emitted JavaScript files
| File | Raw | Gzip |
|---|---|---|
plugin.js | 94.2 KB | 24.0 KB |