higgsfield-websites
Build, edit, and deploy full-stack websites, apps and games via the Higgsfield CLI (`higgsfield website …`). Each is a React 19 + TanStack Start SSR app in one Cloudflare Worker (D1/R2/KV/DO/Containers). THREE product types, picked via `--type` on create: `website` (standalone, no Higgsfield integration — references/website-flow.md), `app` (Sign in with Higgsfield + fnf SDK, Quanta — references/app-flow.md), `game` (realtime multiplayer rooms — references/game-flow.md). Routes to the right flow;
Install
npx skills add https://github.com/higgsfield-ai/skills --skill higgsfield-websitesApp quickstart — the working critical path
Read this FIRST when building a type: "app" product. It is the copy-paste
spine every generation app shares: auth → SDK client → submit/poll → render, plus
the handful of Quanta components you'll reach for. Everything here is real,
correct, and enough to build from — you do not need to open
app/packages/{quanta,fnf,fnf-react}/src/** to confirm any of it. For the full
detail behind each step, the deeper references are cross-linked; for component
props, app/packages/quanta/ai/AGENTS.md has the per-component reference.
Golden rules (violating these is the usual cause of a broken or slow build):
- Don't read package source. Props are in
app/packages/quanta/ai/AGENTS.md; SDK/hooks are inapp/packages/fnf/ai/AGENTS.md+.../fnf-react/ai/AGENTS.md(mirrored byreferences/fnf-sdk.md+references/fnf-react.md). Readingsrc/is the single biggest time sink. If a detail is truly missing, make the reasonable call and letbun run typecheckcatch a mistake. - fnf runs server-side only against
https://fnf.internal— never from the browser, never with tokens in app code (references/auth.md, rule 3 inapp-flow.md). - Always dark, no app header.
data-theme="default-dark"is pinned; the Higgsfield host provides the global header/account chrome. Container ismx-auto w-full max-w-7xl(references/quanta-design.md). - Real backend + real D1, never a mock. (rule 3a in
app-flow.md.)
1. Auth — the /api/user proxy + login/logout
Every app is authenticated. Full contract in references/auth.md; the three
pieces you always write:
A signed-out surface renders a sign-in action that calls login(); a server-side
re-check guards every SDK operation. Do not invent an email/password form.
2. Create the SDK clients (server-side)
One adapter satisfies jobs + media + profile. Import the adapter from
@higgsfield/fnf/workflow-platform (NOT /adapters — that subpath is gone):
The jobs: [...] registry is what gives model/settings their TypeScript
narrowing. Catalog of models in app/packages/fnf/ai/AGENTS.md ("Current SDK
catalog") — e.g. gptImage2→gpt_image_2, nanoBanana2→nano_banana_2,
seedance2_0, kling3_0.
3. Submit (with the confirmation gate) → poll → read the URL
The confirm gate is passed to the adapter factory; it runs once per submit,
after validation, before any network call. Rejecting is a user choice, not a
failure (typed confirmation_rejected).
Prefer safeSubmit across a client/server or iframe boundary and branch on
error.code (survives JSON; instanceof does not):
Common codes: out_of_credits, rate_limit, prompt_nsfw, ip_detected,
job_failed, timeout, validation, confirmation_rejected
(app/packages/fnf/ai/AGENTS.md, "Errors and boundaries").
4. Render the result
generation.results is a single object { rawUrl, minUrl?, thumbnailUrl? },
present only once completed. Do NOT hand-roll URL selection — use the selectors:
In-app, compose result cards from app/src/lib/higgsfield-generation-results.ts
(selectGenerationMedia(generation) returns a resolved image | video | empty
union). A completed generation with no rawUrl must show an explicit
"preview unavailable" state with refresh — never a blank card.
Carry media geometry across the server/client boundary as part of the flat DTO:
prefer result width/height when available, otherwise retain the canonical
submitted aspectRatio. Use that geometry for both the pending skeleton and the
ready card. Preview optimization may change encoding, never composition: preserve
the whole result and do not hardcode square, 16:9, or object-cover for a
generation gallery.
5. React wiring (fnf-react hooks)
useGenerationRun(client, opts?)— drives one submission end-to-end. Returns{ status, generations, isRunning, error, start(input), abort(), reset() };startnever rejects (errors land inerror).clientmust be referentially stable. Best for the "generate" button flow.- Live read of one job:
useQuery(generationQueryOptions(jobClient, jobId, { scopeKey }))— polls every 5s while non-terminal, stops when terminal. (There is nouseGenerationhook.) useAttachments(media, opts?)— upload controller for input images/media:{ items, refs, isUploading, add(files, { role }), settled(), remove(key) }.await controller.settled()yields submit-readyMediaRef[].
6. Media upload — binary never goes through JSON
Never pass File/Blob/ArrayBuffer/base64 through createServerFn input or a
JSON body. Use multipart to an app-local route:
Server reads await file.arrayBuffer(), calls media.upload({ source: bytes, … }),
returns only the MediaRef. Detail in references/fnf-sdk.md ("Media recipes").
7. Server functions + bindings
App data operations run through TanStack server functions or /api/* routes:
Cloudflare bindings (D1 DB, R2 STORAGE, KV KV) are read server-side via
app/src/lib/bindings.server.ts (import { env } from "cloudflare:workers"), and
only exist if declared in app/app.manifest.json ("db": true, etc.). Guard
before use. Runtime detail in references/runtime-and-infra.md.
Return flat, fully-typed DTOs — never a raw SDK object. TanStack Start
compile-time-checks that every server-function return value is serializable, and
SDK types like Generation carry input.settings: Record<string, unknown> —
unknown fails that check and breaks the build. Map the SDK object to a small
DTO with only the fields the client needs ({ id, status, previewUrl, … }),
typed explicitly. Same rule for anything you hand back across the server/client
boundary: no unknown, no class instances, plain JSON-shaped data.
import type React types. In module files there is no React UMD global,
so bare React.ReactNode / React.CSSProperties fail to compile — write
import type { ReactNode, CSSProperties } from "react" and use them unqualified.
8. The Quanta components you'll actually use
All dark, composed — never restyled. Props reference:
app/packages/quanta/ai/AGENTS.md ("Component API reference"). The core set:
Reach for: Button (actions/CTA), Textarea/Input (prompt/fields),
Select/Dropdown (options), Slider/Switch/Chip (settings), Media
(canonical-ratio media), an app-local ratio-preserving frame for mixed generation
results, Card/Glass (surfaces), Grid/VirtualGrid (feeds),
Loader/Progress (busy), Modal/Vault (dialogs/sheets), toast (notices).
For a gap, build a small component from Quanta primitives in
app/src/components/ — never a third-party UI library, never restyle Quanta.
Then follow app-flow.md steps 6–8: deploy → cover + metadata (generate the
cover per references/app-cover.md) → publish (automatically if the user opted in
at intake, else when asked), then suggest the contest.