Skip to main content
@fluid-app/sites is the browser SDK of a member site. It speaks only to your site’s own host, which brokers each call to Fluid. Your code never holds a Fluid token or a database credential.

Import it once

Add the package and import it at the top of your app, before anything renders:
src/main.tsx
Then import fluid where you need it. The SDK also puts the same object on window.fluid.
When the site loads in the storefront, the SDK connects to the storefront and receives the visitor’s sign-in. A call made while it connects waits for it, for up to three seconds, then goes as a guest.

Read who is here

fluid.auth.me() returns the visitor and the site:
me() throws a FluidRequestError when the site’s host answers with an error. The fields in viewer.fields come from the member fields the site declares in config/access.json. See Fluid data the site uses.

Sign in and sign out

  • fluid.auth.signIn({ returnTo }) asks the storefront to send the visitor to its sign-in, then back to returnTo, a path of your site. With no returnTo, the visitor comes back to the page they’re on.
  • fluid.auth.signOut() opens the storefront’s account menu, where the visitor signs out and confirms it.
The storefront acts on both only when the visitor clicked. Call them from a click handler, never on load. Outside a storefront, both do nothing and log a warning.

Call the Fluid API

fluid.api calls the Fluid Member API as the visitor. Pass a Member API path without its version prefix: "/search" calls /api/member/v2026-10/search.
  • fluid.api.get(path, query) sends a GET and returns the parsed JSON. It throws a FluidRequestError for any status that isn’t 2xx.
  • fluid.api.fetch(path, init) takes fetch’s options and returns the response, whatever its status.
  • Both throw a TypeError for a path that isn’t a Member API path, such as a full URL.
Every call acts as the visitor: their own orders, their own team, never anyone else’s. A call is served only when its scope is one the visitor has on this site. Otherwise it answers 403 with scope_not_granted and the scope it needed. Fluid data the site uses says how a site gets a scope. For each operation, see the generated Member API reference.

Handle errors

FluidRequestError carries the answer:

Use the site’s database

fluid.db reads and writes your site’s own tables, declared in db/schema.json. It follows Supabase’s query builder, and every call resolves { data, error }:
The member site database covers the schema, the table rules, every query method and every error code.

Use the storefront’s cart

The storefront’s cart runs in the storefront, not in your frame, so member pricing and the visit’s credit stay the storefront’s. fluid.cart passes each call to it, with the FairShare cart’s own arguments.
add, update, remove and get return a promise of what the cart answered. Each rejects with an Error when the storefront reports a failure, when the site isn’t in a storefront, or when no answer comes within 30 seconds. open, close and checkout log a failure and never throw. The cart API describes FairShare’s arguments. The cart’s HTML controls work in your site too. A click on an element with data-fluid-add-to-cart adds its variants, and data-fluid-cart="open" or "close" opens or closes the drawer:
Enrollment pack controls and data-fluid-cart="toggle" do nothing in a member site. See Declarative HTML controls for the attributes.

Follow Back and Forward

The storefront keeps your site’s history: one entry per page. Your site’s history.pushState calls and same-site link clicks still work, and the SDK reports each move to the storefront. A single-page app’s router listens with fluid.history.onGo. On Back, Forward or a re-sync, the SDK calls your listener with the path and the fragment, instead of reloading the frame:
src/router.ts
path is your site’s own path and query, such as /events/42?tab=agenda, and hash is the fragment, such as #schedule, or null. Move your router there with pushState or replaceState. The SDK doesn’t report a move it started through onGo to the storefront, so Back and Forward never add an entry twice. The call returns a function that removes the listener. With no listener, the SDK moves the frame to the path itself. A multi-page site needs no listener: plain links between its pages work. The frame’s sandbox refuses top-level navigation, so a script can’t move the storefront’s window and target="_top" doesn’t work. Link to storefront pages with a plain <a href>. When the visitor clicks it, the SDK asks the storefront to open it in its own window. fluid.storefront.url(path) returns a storefront address that carries the visit’s credit, so the person who shared the site gets credit for what the visitor buys:
fluid.storefront.url("/shop") returns an address such as https://yoli.com/janedoe/shop. With no credit, or the default home, the address has no credit prefix. It throws a TypeError for a path that leaves the storefront. The SDK also adds the visit’s credit to the storefront links already on your page, and to new ones as the page changes, so a link opened in a new tab keeps it. To keep a link as you wrote it, add data-fluid-credit="off":