Skip to main content

Implementing FormHostProvider

FormHostProvider is a React component you write once in your own project. It connects the Buildocs form to your backend — routing form events through your API and providing an auth token at runtime.

Every integration requires it. Without it, the form cannot load its definition or make any API calls.

GitHub example

A ready-to-use implementation is available in the Workforce Management example on GitHub. In most cases you can copy it as-is and only change the backend URL.

How it works

  1. Your backend exposes a token endpoint. FormHostProvider fetches that token on mount.
  2. You call useBuildocsApi(yourBackendUrl, token) to get pre-wired HTTP methods that route to your backend.
  3. You build a HostBridge object mapping each form event to a method call.
  4. You render BuildocsHostProvider with the bridge, wrapping your children.

The form definition is still fetched directly from Buildocs using useBuildocsApi() (no custom URL).

Step 1 — Create the file

Create src/provider/FormHostProvider.tsx and paste in the boilerplate below.

import React, { useEffect, useState } from 'react';

import {
type TLoadForm,
type HostBridge,
type RunEventArgs,
type UploadArgs,
ChunkRequestArgs,
DeleteFileEventArgs,
DownloadFileEventArgs,
FileMetaEventArgs,
GetLinkedFileMetaEventArgs,
BuildocsHostProvider,
useBuildocsApi,
useViewport,
} from '@buildocsdev/sdk';

interface FormHostProviderProps {
readonly children: React.ReactNode;
}

export function FormHostProvider({ children }: FormHostProviderProps) {
const { isMobileDevice, width, height } = useViewport();
const [token, setToken] = useState<string | undefined>(undefined);

const buildocsApi = useBuildocsApi();

const myApiUrl = 'https://your-backend.com/api/v1/formspublic';
const myApi = useBuildocsApi(myApiUrl, token);

useEffect(() => {
fetch(`${myApiUrl}/token`, { method: 'POST' })
.then(r => r.json())
.then((d: { token: string }) => setToken(d.token))
.catch(e => console.error('Failed to fetch token:', e));
}, []);

const hostBridge: HostBridge = {
viewPort: { isMobileDevice, width, height },

runFormEvent: async (args: RunEventArgs) => {
return await myApi.runEvent(args, '/runevent');
},
runFormUploadFiles: async (args: UploadArgs) => {
return await myApi.uploadFiles(args, '/uploadfiles');
},
runFormDeleteFileEvent: async (args: DeleteFileEventArgs) => {
return await myApi.deleteFile(args, '/deletefile');
},
runFormDownloadFileEvent: async (args: DownloadFileEventArgs) => {
return await myApi.downloadFile(args, '/downloadfile');
},
runFormGetLinkedFileEvent: async (args: GetLinkedFileMetaEventArgs) => {
return await myApi.getLinkedFile(args, '/getlinkedfile');
},
runFormGetLinkedFileMetaEvent: async (args: GetLinkedFileMetaEventArgs) => {
return await myApi.getLinkedFileMetaEvent(args, '/getlinkedfilemeta');
},
runFormGetLinkedFileByChunks: async (args: ChunkRequestArgs) => {
return await myApi.getLinkedFileByChunks(args, '/getlinkedfilebychunks');
},
runFormGetPresignedUrl: async (args: FileMetaEventArgs) => {
return await myApi.getPresignedUrl(args, '/getpresignedurl');
},
runLoadFormEvent: async (args: TLoadForm) => {
const buildocsApiResponse = await buildocsApi.fetchFormDefinitionWithMeta(args.formCode);
const result = await myApi.runLoadForm({...args, ...buildocsApiResponse?.payload}, '/loadform');
return result;
},
};

return (
<BuildocsHostProvider hostBridge={hostBridge}>
{children}
</BuildocsHostProvider>
);
}

Step 2 — Wrap your app

FormHostProvider must be nested inside BuildocsProvider. Place both providers high enough in your tree to cover all Form components.

import { BuildocsProvider } from '@buildocsdev/sdk';
import { Form } from '@buildocsdev/sdk/form';
import { FormHostProvider } from './provider/FormHostProvider';

function App() {
return (
<BuildocsProvider apiKey="[project-api-key]">
<FormHostProvider>
<Form params={{ formCode: "your-form-code", guid: "new" }} />
</FormHostProvider>
</BuildocsProvider>
);
}

Token authentication

The token is fetched from your backend on mount and passed to useBuildocsApi as its second argument. All subsequent event calls include it automatically.

const myApi = useBuildocsApi(myApiUrl, token);

Your backend's /token endpoint should return:

{ "token": "<your-auth-token>" }

useBuildocsApi

useBuildocsApi returns pre-configured HTTP methods. The boilerplate uses it twice:

InstancePurpose
useBuildocsApi()Fetches the form definition directly from Buildocs
useBuildocsApi(myApiUrl, token)Routes all form events through your own backend with Bearer token auth

Each method accepts a customPath as its second argument. The final URL is baseUrl + customPath:

// POST https://your-backend.com/api/v1/formspublic/runevent
return await myApi.runEvent(args, '/runevent');

HostBridge methods

Each method maps to a form action and must have a corresponding endpoint in your backend.

HostBridge methoduseBuildocsApi methodDefault path
runFormEventrunEvent/runevent
runFormUploadFilesuploadFiles/uploadfiles
runFormDeleteFileEventdeleteFile/deletefile
runFormDownloadFileEventdownloadFile/downloadfile
runFormGetLinkedFileEventgetLinkedFile/getlinkedfile
runFormGetLinkedFileMetaEventgetLinkedFileMetaEvent/getlinkedfilemeta
runFormGetLinkedFileByChunksgetLinkedFileByChunks/getlinkedfilebychunks
runFormGetPresignedUrlgetPresignedUrl/getpresignedurl
runLoadFormEventfetchFormDefinitionWithMeta— (direct to Buildocs)

Pre-populating form data

Pass existing data as the second argument to fetchFormDefinitionWithMeta. Keys must match the Name of each field in the Form Builder exactly.

runLoadFormEvent: async (args: TLoadForm) => {
const existingData = await myDb.getRecord(args.guid);
return await buildocsApi.fetchFormDefinitionWithMeta(args.formCode, existingData);
},

Customization patterns

Intercept events before sending

Add logging, validation, or extra headers before the API call.

runFormEvent: async (args: RunEventArgs) => {
console.log('Form event fired:', args.widgetName, args.widgetEvent);
return await myApi.runEvent(args, '/runevent');
},