appsmithorg/appsmith · error · Error
FormControl compound components cannot be rendered outside t
Error message
FormControl compound components cannot be rendered outside the FormControl component
What it means
Intended to fire from useFormControlContext() in the Appsmith Design System when a FormControl compound component (e.g. FormLabel, FormHelperText, FormInput) is rendered without a FormControl ancestor. IMPORTANT DEFECT grounded in the source: FormControlContext is created with createContext({...}) passing a non-null default object ({isRequired:false,isDisabled:false,size:"sm"}), so useContext always returns a truthy value and the `if (!context)` guard is DEAD CODE — this error can never actually be thrown at runtime as written. The intent is real; the implementation does not achieve it.
Source
Thrown at app/client/packages/design-system/ads/src/FormControl/FormControl.context.ts:16
import { createContext, useContext } from "react";
import type { FormControlProps } from "./FormControl.types";
export const FormControlContext = createContext<
Pick<FormControlProps, "isRequired" | "isDisabled" | "size">
>({
isRequired: false,
isDisabled: false,
size: "sm",
});
export const useFormControlContext = () => {
const context = useContext(FormControlContext);
if (!context) {
throw new Error(
"FormControl compound components cannot be rendered outside the FormControl component",
);
}
return context;
};
export const FormControlProvider = FormControlContext.Provider;
View on GitHub (pinned to 8cd9021c24)
Solutions
- Always render compound components inside <FormControl> so context is provided explicitly.
- Fix the underlying defect: pass null as the createContext default (createContext<Pick<...> | null>(null)) so the guard becomes live, matching the Sidebar pattern.
- If fixing the library, audit all consumers once the throw becomes reachable — currently any out-of-tree usage has been silently passing.
- Add a unit test rendering a compound component without a provider to lock in the intended behavior.
Example fix
// before (dead guard)
export const FormControlContext = createContext({ isRequired:false, isDisabled:false, size:"sm" });
export const useFormControlContext = () => {
const context = useContext(FormControlContext);
if (!context) { throw new Error(...); } // unreachable
return context;
};
// after (live guard)
export const FormControlContext = createContext<Pick<FormControlProps,"isRequired"|"isDisabled"|"size"> | null>(null);
export const useFormControlContext = () => {
const context = useContext(FormControlContext);
if (!context) { throw new Error("FormControl compound components cannot be rendered outside the FormControl component"); }
return context;
}; Defensive patterns
Strategy: type-guard
Type guard
// NOTE: current default is non-null so this never throws; fix the lib first.
// After fix (createContext<T|null>(null)):
function hasFormControlContext<T>(ctx: T | null): ctx is T { return ctx !== null; } Try / catch
// Wrap risky rendering; today this is dead code, so add the provider regardless.
try { return renderCompound(); } catch (e) {
if (/FormControl compound components/i.test(e.message)) return <FormControl>{renderCompound()}</FormControl>;
throw e;
} Prevention
- Always nest compound components in <FormControl>.
- Fix the createContext default to null so the guard is live and surfaces misuse.
- Add a story/test that renders a compound outside the provider to verify the throw.
When it happens
Trigger: Render a FormControl.* subcomponent outside <FormControl>. Intended trigger only — in practice the non-null default suppresses the throw, so the component silently renders with default context values instead.
Common situations: Copying a Form subcomponent into a story or test without the FormControl wrapper; refactoring a form and hoisting a label out of FormControl; using the design system primitives standalone. Developer expects a clear error but gets silent default sizing.
Related errors
- useSidebar must be used within a SidebarProvider.
- Missing basePageId. If you are trying to set href inside a r
- Mail not sent! APPSMITH_MAIL_ENABLED env val is disabled, pl
- error
AI-assisted analysis of appsmithorg/appsmith@8cd9021c24 (2026-08-12).
Data as JSON: /api/errors/a216d242e259a410.
Report an issue: GitHub.