Render an existing form inside your own page, connect the shared providers, and handle loading, submission errors, success messages, and redirects.

You have designed a form and saved its schema. The next job is to place it inside your contact, onboarding, or feedback page without exposing the form editor to visitors. The public renderer needs the saved form's slug and the application's existing BTST runtime.
BTST 3.0.2 exports FormRenderer for this job. It fetches a saved form, renders its fields, submits answers, and displays the configured success state. This guide focuses on embedding and visitor behavior; the server submission validation guide covers the separate write boundary.
Place the renderer below the existing React Query and BTST providers, with the Form Builder client plugin registered in the resolved stack. A plain React component import does not configure its API base, permissions, or overrides.
Keep the page route and the form slug separate. A page at /contact can render a form whose slug is customer-enquiry; the page does not need to use the editor's route. Follow the Form Builder setup for the shared backend, client plugin, and provider wiring.
This component receives an ordinary string prop, so its framework route can resolve parameters before rendering it:
"use client";
import { useState } from "react";
import { FormRenderer } from "@btst/stack/plugins/form-builder/client/components";
export function PublicEnquiryForm({ slug }: { slug: string }) {
const [submissionFailed, setSubmissionFailed] = useState(false);
return (
<section aria-label="Enquiry form">
{submissionFailed && (
<p role="alert">
We could not send your response. Check your answers and try again.
</p>
)}
<FormRenderer
slug={slug}
submitButtonText="Send enquiry"
successMessage="Thanks. Your enquiry has been received."
onError={() => setSubmissionFailed(true)}
onSuccess={() => setSubmissionFailed(false)}
/>
</section>
);
}
The snippet assumes the surrounding providers and an active saved form. It does not create the form or configure email delivery. In an app where the slug changes without unmounting this component, render it with key={slug} from the parent so a previous form's local success or error state is not reused for the next form.
LoadingComponent and ErrorComponent customize loading and form-fetch failures. onError handles a failed submission. These are different stages: an active form may load successfully while its submission is rejected by validation, authorization, or a temporary network problem.
The example displays a general submission error without putting raw server messages or submitted answers into the page's logs. For field-specific validation, inspect the renderer's existing behavior with your actual schema before adding a second set of messages.
The released renderer checks that the saved form has status active; it displays an error for inactive forms. It also checks the rendered form and submission permissions. Those browser checks do not replace the backend rules. Public form reading and answer submission should be explicitly allowed only as intended; form editing and viewing stored submissions can stay restricted.
The renderer can use a success message from the saved form or a successMessage prop override. If the submission response supplies a redirect URL, the released component invokes onSuccess and then navigates to that URL.
Choose whether this form should show an inline confirmation or leave the page. If the saved configuration includes a redirect, a custom success message does not cancel that redirect. Verify the destination in the editor and application policy, and test the live navigation using non-sensitive test answers.
Use onSuccess for optional UI or a privacy-preserving completion event. Avoid throwing from that callback: in this release it runs inside the submission handler's try block, so a callback error can be presented as a failure after the server has already accepted the answer. A browser callback is also not a durable delivery guarantee for email or downstream processing.
fieldComponents lets you provide custom field renderers. The public form and editor need to agree on the field metadata those components understand. Test labels, keyboard navigation, error descriptions, and all required input types, including any multi-step structure.
Before release, test an active form, an inactive form, a missing slug, a rejected submission, a successful retry, and the configured success destination. Confirm that anonymous visitors can submit only what your server policy permits and cannot read submissions or edit forms. Then use the Form Builder reference for the complete props, permission configuration, and customization options.