Skip to content

Provider Setup

React UI components usually need:

  1. SpaceInvoicesProvider — Initializes the modular SDK runtime
  2. EntitiesProvider — Optional active-entity state for multi-entity apps

CreateInvoiceScreen is self-contained for standalone consumers: it renders the Save and Save as Draft actions when no host footer provider exists. If the host already has a FormFooterProvider, the screen registers with that provider and the host should render its existing StickyFormFooter once. CreateInvoiceForm by itself should either be rendered under that provider or pass its onFooterStateChange state to a host-owned footer.

Full Setup

Provider Setuptypescript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { EntitiesProvider } from "./providers/entities-provider";
import { SpaceInvoicesProvider } from "./providers/space-invoices-provider";

const queryClient = new QueryClient();
const activeAccountId = "acc_123"; // Required for multi-account user-token flows

export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <SpaceInvoicesProvider
        accessToken={() => localStorage.getItem("access_token") ?? ""}
        accountId={activeAccountId}
        basePath="https://eu.spaceinvoices.com"
      >
        <EntitiesProvider>
          {/* Your app components */}
          <Router />
        </EntitiesProvider>
      </SpaceInvoicesProvider>
    </QueryClientProvider>
  );
}

SpaceInvoicesProvider

The SpaceInvoicesProvider initializes the Space Invoices runtime and makes authenticated SDK calls work inside copied components.

For multi-account browser apps using a user token, pass accountId explicitly. API-key, embed, and single-account flows can leave it unset.

Props

PropTypeDescription
basePathstringAPI base URL
accessTokenstring | () => string | Promise<string>Token value or async token resolver
accountIdstring | nullOptional active account ID
clientNamestringOptional client header override
onUnauthorized(response: Response) => voidOptional auth failure handler

Getting the Token

How you get the token depends on your auth setup:

Token Providerstypescript
// From localStorage
const fromLocalStorage = () => localStorage.getItem("si_token");

// From a cookie
const _fromCookie = () => document.cookie.match(/token=([^;]+)/)?.[1] ?? null;

// From an auth library (e.g., NextAuth)
const _fromSession = (session) => session?.accessToken ?? null;

// For multi-account user-token flows, also pass the active account explicitly.
const activeAccountId = "acc_123";

// Usage in SpaceInvoicesProvider:
<SpaceInvoicesProvider
  basePath="https://eu.spaceinvoices.com"
  accessToken={fromLocalStorage}
  accountId={activeAccountId}
>
  {/* or getAccessToken={fromCookie} */}
  {/* or accessToken={() => fromSession(session) ?? ""} */}
</SpaceInvoicesProvider>;

Multi-account apps

If your host app lets a user switch between multiple accounts, pass the currently selected account ID into SpaceInvoicesProvider. The React UI package does not infer account context from browser cookies.

EntitiesProvider

The EntitiesProvider manages entity selection for multi-entity apps. If your host app already knows the entity, prefer explicit entityId props and skip this provider.

Hosts that need branded loading UI can pass a fallbackLoading React node; when omitted, the provider uses its generic accessible loading overlay.

Using Active Entity

Using Active Entitytypescript
import { useEntities } from "@/providers/entities-provider";

function _InvoicesPage() {
  const { activeEntity, isLoading } = useEntities();

  if (isLoading) return <Spinner />;
  if (!activeEntity) return <NoEntitySelected />;

  return <InvoiceListTable entityId={activeEntity.id} onRowClick={(inv) => navigate(`/invoices/${inv.id}`)} />;
}

Entity Switching

Entity Switchingtypescript
import { useEntities } from "@/providers/entities-provider";

function _EntitySelector() {
  const { entities, activeEntity, setActiveEntity } = useEntities();

  return (
    <Select
      value={activeEntity?.id}
      onValueChange={(id) => {
        const entity = entities.find((e) => e.id === id);
        if (entity) setActiveEntity(entity);
      }}
    >
      {entities.map((entity) => (
        <SelectItem key={entity.id} value={entity.id}>
          {entity.name}
        </SelectItem>
      ))}
    </Select>
  );
}

Accessing the SDK Directly

Components use the SDK internally, but for custom code prefer direct modular SDK imports instead of a provider hook:

Direct SDK Accesstypescript
import { invoices } from "@spaceinvoices/js-sdk/sdk";

function _CustomComponent() {
  const handleCustomAction = async () => {
    // Prefer direct modular SDK imports for custom operations
    const result = await invoices.renderPdf(invoiceId);
    downloadBlob(result, "invoice.pdf");
  };

  return <Button onClick={handleCustomAction}>Download PDF</Button>;
}

Next Steps