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

TypeScriptfeedback-dialog.ts
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

TypeScriptfeedback-dialog.tsx
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:

TypeScriptapp-feedback-dialog.tsx
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:

TypeScriptfeedback-sheet.tsx
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.