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.
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
- Your backend exposes a token endpoint.
FormHostProviderfetches that token on mount. - You call
useBuildocsApi(yourBackendUrl, token)to get pre-wired HTTP methods that route to your backend. - You build a
HostBridgeobject mapping each form event to a method call. - You render
BuildocsHostProviderwith 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:
| Instance | Purpose |
|---|---|
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 method | useBuildocsApi method | Default path |
|---|---|---|
runFormEvent | runEvent | /runevent |
runFormUploadFiles | uploadFiles | /uploadfiles |
runFormDeleteFileEvent | deleteFile | /deletefile |
runFormDownloadFileEvent | downloadFile | /downloadfile |
runFormGetLinkedFileEvent | getLinkedFile | /getlinkedfile |
runFormGetLinkedFileMetaEvent | getLinkedFileMetaEvent | /getlinkedfilemeta |
runFormGetLinkedFileByChunks | getLinkedFileByChunks | /getlinkedfilebychunks |
runFormGetPresignedUrl | getPresignedUrl | /getpresignedurl |
runLoadFormEvent | fetchFormDefinitionWithMeta | — (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');
},