Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.flow-board.co/llms.txt

Use this file to discover all available pages before exploring further.

Flowboard already handles the common actions used in most flows:
  • next
  • previous
  • finish
  • deeplink
  • weblink
  • rate_app
  • request_permission
  • jump_to
Use a custom action when one step needs to call your own app logic, such as opening a checkout flow, saving data to your backend, or triggering a native feature. When Flowboard sees an action it does not handle internally, it calls customActionBuilder(action, ctx, data).
Native iOS uses a different surface: onCustomAction(context) handles only custom actions, and onAction(context) lets you observe any action before Flowboard performs its default behavior.

What the handler receives

Your handler receives:
  • action: the action name from the flow, such as open_checkout
  • ctx: the same Flowboard context used by custom screens
  • data: a merged payload containing the component properties and any actionData
That means actionData is the right place for business inputs, while component properties can still be useful for labels or presentation defaults. For native iOS, the equivalent context is FlowboardActionContext, which gives you:
  • ctx.action.payload: the merged custom action payload
  • ctx.values: the current collected flow values
  • ctx.screenID: the active screen id
  • ctx.sourceComponentID: the tapped component id when available

Basic flow shape

{
  "type": "button",
  "action": "open_checkout",
  "actionData": {
    "plan": "pro",
    "successScreenId": "signup"
  },
  "properties": {
    "label": "Start trial"
  }
}

Simple example

Start with one action name and one small handler.
import { Alert } from 'react-native';
import { Flowboard } from 'flowboard-react';

export async function startFlow() {
  await Flowboard.launchOnboarding({
    customActionBuilder: (action, ctx, data) => {
      if (action !== 'open_checkout') {
        return;
      }

      const plan = typeof data?.plan === 'string' ? data.plan : 'free';

      Alert.alert('Checkout', `Open checkout for ${plan}`);
      ctx.onNext();
    },
  });
}
On native iOS, custom actions are side-effect hooks. They do not expose ctx.onNext() or ctx.onJumpTo(), so keep navigation inside the flow with standard Flowboard actions such as next, finish, or jump_to.
Keep the custom action name stable and explicit, such as open_checkout, create_account, or apply_referral_code. Avoid overloaded names like submit.
For production, use one central router with a switch and move each action into its own helper. This is the cleanest setup when a flow can trigger multiple business actions. Recommended structure:
  • A thin customActionBuilder
  • One routeFlowboardAction function with a switch
  • Small helper functions that parse data, call your services, and decide whether to use ctx.onNext(), ctx.onFinish(), or ctx.onJumpTo()
For native iOS, keep the same router idea, but return .handled or .performDefault from onCustomAction(...) instead of calling navigation callbacks.
// src/flowboard/customActions.ts
import type { FlowboardContext } from 'flowboard-react';

type ActionPayload = Record<string, unknown> | undefined;

function readString(data: ActionPayload, key: string): string {
  const value = data?.[key];
  return typeof value === 'string' ? value.trim() : '';
}

async function handleOpenCheckout(
  ctx: FlowboardContext,
  data: ActionPayload
) {
  const plan = readString(data, 'plan') || 'free';
  const successScreenId = readString(data, 'successScreenId');

  await billing.presentCheckout({
    plan,
    customerEmail:
      typeof ctx.formData.email === 'string' ? ctx.formData.email : undefined,
  });

  if (successScreenId) {
    ctx.onJumpTo(successScreenId);
    return;
  }

  ctx.onNext();
}

async function handleCreateAccount(
  ctx: FlowboardContext,
  data: ActionPayload
) {
  await api.post('/signup', {
    email: ctx.formData.email,
    password: ctx.formData.password,
    referralCode: readString(data, 'referralCode') || undefined,
  });

  ctx.onNext();
}

export async function routeFlowboardAction(
  action: string,
  ctx: FlowboardContext,
  data: ActionPayload
) {
  switch (action) {
    case 'open_checkout':
      await handleOpenCheckout(ctx, data);
      return;
    case 'create_account':
      await handleCreateAccount(ctx, data);
      return;
    default:
      console.warn(`Unhandled Flowboard action: ${action}`);
  }
}

// src/features/onboarding/launchOnboarding.ts
import { Flowboard } from 'flowboard-react';
import { routeFlowboardAction } from '../../flowboard/customActions';

export async function launchOnboarding() {
  await Flowboard.launchOnboarding({
    customActionBuilder: (action, ctx, data) => {
      void routeFlowboardAction(action, ctx, data);
    },
  });
}
Replace billing and api with your own services. On native iOS, the important contract is the action name, the parsed payload, and whether you return .handled or .performDefault.

Practical guidance

Use custom actions when Flowboard should trigger:
  • A purchase flow from your billing SDK
  • Account creation or profile updates in your backend
  • Native features that are specific to your app
  • Branching decisions that depend on both collected flow values and action-specific payload
Use actionData for business parameters and ctx.formData or ctx.values for answers already collected in the flow.