Skip to main content
@cesto/react wraps @cesto/web-sdk — you don’t need to install both. It requires React 18 or later, is client-only, and ships a 'use client' banner, so you can import it straight from a server component. Callbacks, being functions, still have to live in a client component.

<CestoProvider>

Supplies the key, theme, mode, and default callbacks to everything below it, and owns a single shared loader instance. Wrap your app once:
Provider callbacks run before any per-hook or per-call callback, so the provider is the right place for analytics and the call site is the right place for UI state. Nesting is supported — the nearest provider wins. Inline arrow callbacks and inline theme literals are safe: both are latched by content, not identity, so they never churn the context value or recreate the loader.
When the provider unmounts, an open dialog is closed. The overlay is attached to document.body by the loader, so a route change would otherwise strand it on the page. Closing only tears down the surface — an execution already in flight continues server-side.

<CestoInvestButton>

The CTA. Styled out of the box, themed from the same accent and radius as the dialog it opens, and every escape hatch is first-class.
It also forwards a ref to the underlying element and spreads every native button attribute (type defaults to "button"). While a flow is open the element carries data-cesto-state="open", which you can style against.

Using your own design system

asChild merges the trigger onto your element instead of rendering a <button>:
asChild opts out of the bundled spinner too — the child element’s content is yours, and is passed through untouched. Read isOpen from useCestoInvest if you want your own pending state.

Using your own CSS

The bundled button inherits your page’s font-family on purpose, so it never lands as a fallback-font CTA in someone else’s typography. Only the accent, its paired text ink, and the radius come from the theme.

Gating on your own validation

Your onClick runs first. Call preventDefault() to suppress the flow:

Strict CSP

The stylesheet is injected at runtime, which a style-src without 'unsafe-inline' blocks — the button would render unstyled. Import the stylesheet instead and mark the document so the runtime injection stands down:
Both stylesheets are emitted from the same source at build time, so importing without the marker is harmless — just a duplicate, byte-identical rule set. The raw CSS is also exported as CESTO_BUTTON_CSS if you’d rather inline it yourself.

useCestoInvest()

The hook behind the button. Use it when you want the flow but not the CTA.
Returns OptionsapiKey, baseUrl, theme, mode, plus any callbacks. Passing apiKey, baseUrl, or theme gives the hook its own loader rather than the provider’s shared one. The hook works with or without a provider. Without one, pass apiKey directly:
status === 'success' and isOpen === true is a normal, reachable combination: the flow stays open after a success until the user dismisses it. Track the two separately rather than deriving one from the other.
Call invest() synchronously in the event handler — no await before it. See popup blockers.

Errors before the flow opens

If the loader can’t be built — a missing key, a rejected baseUrl, or a non-browser environment — invest() does not throw. It fires onError with code: 'config_error', sets status to 'error', and returns an inert session. That keeps a misconfiguration observable in your own error handling instead of crashing a render.

Next.js App Router

The package is client-only and carries a 'use client' banner, so a server component can import and render <CestoProvider> and <CestoInvestButton> directly — no wrapper needed. Callbacks are functions and cannot cross the server boundary, so put any component that passes them in a 'use client' file:
A publishable key belongs in a public env var (NEXT_PUBLIC_…). It is meant to be in the bundle — see API keys.

Exports

Next

Events

Every callback, and the order provider → hook → call site.

Theming

The tokens shared by the button and the dialog.