appsmithorg/appsmith · error · Error
FormControl compound components cannot be rendered outside…
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.
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…
- error
- Mail not sent! APPSMITH_MAIL_ENABLED env val is disabled…
AI-assisted analysis of appsmithorg/appsmith@8cd9021c24 (2026-08-12).
Data as JSON: /api/errors/a216d242e259a410.
Report an issue: GitHub.
Appendix: 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)