Containers
Render flows inside your own dialog, sheet, drawer, or panel.
- Last reviewed
Containers
By default, the widget uses the SDK's built-in presentation. Use a custom container when it should appear inside your own dialog, sheet, drawer, panel, or sidebar.
On web, this is the most advanced level of visual control. In React Native, it is the recommended way to match the rest of your app's native presentation. See Look & feel for the broader picture.
How it works
Instead of the widget managing its own positioning, you provide a DOM element
(or a React ref) on the web, or render FlowContent inside your own React
Native view. You control the surrounding UI — the dialog chrome, the backdrop,
the close button.
JavaScript SDK
import { createClient } from "@getuserfeedback/sdk";const client = createClient({ apiKey: "YOUR_API_KEY" });const flow = client.flow("YOUR_FLOW_ID");const container = document.getElementById("feedback-dialog-body");if (container) {flow.open({ container });}Both fire-and-forget and await work correctly here. See
JavaScript SDK reference.
See JavaScript SDK reference for
the full open() options.
React SDK
import * as Dialog from "@radix-ui/react-dialog";import { useFlow } from "@getuserfeedback/react";function FeedbackDialog() {const { containerRef, setOpen, shouldRenderContainer } = useFlow({flowId: "YOUR_FLOW_ID",container: "custom",});return (<Dialog.Root onOpenChange={setOpen} open={shouldRenderContainer}><Dialog.Trigger>Open feedback</Dialog.Trigger><Dialog.Content><divref={containerRef}style={{ width: "100%" }}/></Dialog.Content></Dialog.Root>);}Loader sizes and animates its child view surface. Your container only needs to
express the space it makes available, such as a max-width constraint.
See React SDK reference for the full
useFlow() options.
To provide one container for flows using the default presentation in a provider
subtree, use useFlowContainer() instead:
import * as Dialog from "@radix-ui/react-dialog";import { useFlowContainer } from "@getuserfeedback/react";export function FeedbackDialog() {const { close, containerRef, shouldRenderContainer } = useFlowContainer();return (<Dialog.Rootopen={shouldRenderContainer}onOpenChange={(open) => {if (!open) void close();}}><Dialog.Content><divref={containerRef}style={{ width: "100%" }}/></Dialog.Content></Dialog.Root>);}Mount this component below GetUserFeedbackProvider. It becomes the default
container for flows opened by targeting and from code. Mount exactly one
component that calls useFlowContainer() per logical client. Providers with the
same API key and targeting context reuse that client. Use the per-flow
useFlow({ container: "custom" }) pattern instead when each flow needs its own
container; combining both patterns in one provider is not currently supported.
React Native SDK
Use useFlowContainer() to control your app's sheet, and render exactly one
FlowContent inside it:
import {FlowContent,useFlowContainer,} from "@getuserfeedback/react-native";import { AppSheet } from "./app-sheet";export function FeedbackSheet() {const { close, shouldRenderContainer } = useFlowContainer();return (<AppSheetopen={shouldRenderContainer}onDismiss={() =>void close().catch((error) =>console.error("Unable to close feedback", error),)}><FlowContent /></AppSheet>);}AppSheet represents your app's presentation component. Keep FlowContent
mounted while the sheet is closed; don't render it conditionally. It measures
the active flow and sizes itself, so you don't need to read or apply width and
height yourself. close() returns a promise that resolves after the active
flows close. See the React Native SDK reference
for the provider, client, and container API shapes.