Specialized utilities for React applications including context creation with error handling, children filtering, prop manipulation patterns, and type definitions for common React patterns. These utilities provide enhanced developer experience and type safety for React component development.
Enhanced React context creation with built-in error handling and TypeScript support.
/**
* Creates React context with enhanced error handling and TypeScript support
* @param options - Configuration options for context creation
* @returns Tuple containing [Provider, useContext hook, Context object]
*/
function createContext<T>(options: CreateContextOptions<T>): CreateContextReturn<T>;
interface CreateContextOptions<T> {
/** Whether to throw error when context is used outside provider */
strict?: boolean;
/** Name of the hook for error messages */
hookName?: string;
/** Name of the provider for error messages */
providerName?: string;
/** Custom error message */
errorMessage?: string;
/** Context name for debugging */
name?: string;
/** Default value for the context */
defaultValue?: T;
}
type CreateContextReturn<T> = [
React.Provider<T>,
() => T,
React.Context<T>
];Usage Examples:
import { createContext } from "@chakra-ui/utils";
// Basic theme context
interface ThemeContextValue {
theme: "light" | "dark";
toggleTheme: () => void;
}
const [ThemeProvider, useTheme, ThemeContext] = createContext<ThemeContextValue>({
strict: true,
hookName: "useTheme",
providerName: "ThemeProvider",
errorMessage: "useTheme must be used within a ThemeProvider"
});
function App() {
const [theme, setTheme] = React.useState<"light" | "dark">("light");
const toggleTheme = () => {
setTheme(prev => prev === "light" ? "dark" : "light");
};
return (
<ThemeProvider value={{ theme, toggleTheme }}>
<Header />
<Main />
</ThemeProvider>
);
}
function Header() {
const { theme, toggleTheme } = useTheme(); // Type-safe and error-handled
return (
<header className={`header header--${theme}`}>
<button onClick={toggleTheme}>
Switch to {theme === "light" ? "dark" : "light"} mode
</button>
</header>
);
}
// Modal context with default value
interface ModalContextValue {
isOpen: boolean;
open: () => void;
close: () => void;
}
const [ModalProvider, useModal] = createContext<ModalContextValue>({
strict: false,
name: "ModalContext",
defaultValue: {
isOpen: false,
open: () => {},
close: () => {}
}
});
// Form context with custom error
interface FormContextValue {
values: Record<string, any>;
errors: Record<string, string>;
setValue: (name: string, value: any) => void;
}
const [FormProvider, useFormContext] = createContext<FormContextValue>({
strict: true,
hookName: "useFormContext",
providerName: "Form",
errorMessage: "Form fields must be used within a Form component"
});
function Form({ children, onSubmit }: FormProps) {
const [values, setValues] = React.useState({});
const [errors, setErrors] = React.useState({});
const setValue = (name: string, value: any) => {
setValues(prev => ({ ...prev, [name]: value }));
// Clear error when value changes
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: "" }));
}
};
return (
<FormProvider value={{ values, errors, setValue }}>
<form onSubmit={onSubmit}>
{children}
</form>
</FormProvider>
);
}
function TextInput({ name, placeholder }: { name: string; placeholder?: string }) {
const { values, errors, setValue } = useFormContext();
return (
<div>
<input
value={values[name] || ""}
onChange={(e) => setValue(name, e.target.value)}
placeholder={placeholder}
/>
{errors[name] && <span className="error">{errors[name]}</span>}
</div>
);
}Utilities for working with React children elements.
/**
* Filters React children to only include valid React elements
* @param children - React children to filter
* @returns Array of valid React elements
*/
function getValidChildren(children: React.ReactNode): React.ReactElement[];Usage Examples:
import { getValidChildren } from "@chakra-ui/utils";
// Stack component that handles various children types
interface StackProps {
children: React.ReactNode;
spacing?: string | number;
direction?: "row" | "column";
}
function Stack({ children, spacing = "1rem", direction = "column" }: StackProps) {
const validChildren = getValidChildren(children);
return (
<div
style={{
display: "flex",
flexDirection: direction,
gap: spacing
}}
>
{validChildren.map((child, index) => (
React.cloneElement(child, {
key: child.key || index,
// Add stack-specific props
"data-stack-item": true
})
))}
</div>
);
}
// Usage - only valid elements are rendered
<Stack spacing="2rem">
<div>Item 1</div>
{false && <div>This won't render</div>}
{null}
<div>Item 2</div>
{"String children are filtered out"}
<div>Item 3</div>
</Stack>
// Tabs component that processes tab children
interface TabsProps {
children: React.ReactNode;
defaultTab?: number;
}
function Tabs({ children, defaultTab = 0 }: TabsProps) {
const [activeTab, setActiveTab] = React.useState(defaultTab);
const validChildren = getValidChildren(children);
// Extract tab titles and content
const tabs = validChildren.map((child) => ({
title: child.props.title || `Tab ${validChildren.indexOf(child) + 1}`,
content: child.props.children
}));
return (
<div className="tabs">
<div className="tab-list">
{tabs.map((tab, index) => (
<button
key={index}
className={`tab ${index === activeTab ? "tab--active" : ""}`}
onClick={() => setActiveTab(index)}
>
{tab.title}
</button>
))}
</div>
<div className="tab-content">
{tabs[activeTab]?.content}
</div>
</div>
);
}
// Tab component for use with Tabs
function Tab({ title, children }: { title: string; children: React.ReactNode }) {
return <>{children}</>;
}
// Usage
<Tabs defaultTab={1}>
<Tab title="Overview">
<p>Overview content here</p>
</Tab>
<Tab title="Details">
<p>Details content here</p>
</Tab>
{/* Invalid children are filtered out */}
{false && <Tab title="Hidden">Hidden content</Tab>}
<Tab title="Settings">
<p>Settings content here</p>
</Tab>
</Tabs>Utilities for splitting and organizing component props.
/**
* Splits props into multiple objects based on key arrays
* @param props - Source props object to split
* @param keys - Variable number of key arrays to group props
* @returns Array of objects corresponding to each key group
*/
function splitProps(props: Dict, ...keys: Key[]): Dict[];
type Dict<T = any> = Record<string, T>;
type Key = string | string[];Usage Examples:
import { splitProps } from "@chakra-ui/utils";
// Complex button component with multiple prop categories
interface ButtonProps {
// Visual props
variant?: "solid" | "outline" | "ghost";
size?: "sm" | "md" | "lg";
colorScheme?: string;
// Behavior props
onClick?: () => void;
onFocus?: () => void;
onBlur?: () => void;
// DOM props
id?: string;
className?: string;
"data-testid"?: string;
// Content
children: React.ReactNode;
leftIcon?: React.ReactElement;
rightIcon?: React.ReactElement;
}
function Button(props: ButtonProps) {
const [visualProps, behaviorProps, domProps, contentProps] = splitProps(
props,
["variant", "size", "colorScheme"], // Visual styling
["onClick", "onFocus", "onBlur"], // Event handlers
["id", "className", "data-testid"], // DOM attributes
["children", "leftIcon", "rightIcon"] // Content
);
const buttonStyles = computeButtonStyles(visualProps);
return (
<button
{...domProps}
{...behaviorProps}
style={buttonStyles}
className={cx("button", domProps.className)}
>
{contentProps.leftIcon}
{contentProps.children}
{contentProps.rightIcon}
</button>
);
}
// Input component with form integration
interface InputProps {
// Form props
name?: string;
value?: string;
defaultValue?: string;
required?: boolean;
// Validation props
isInvalid?: boolean;
errorMessage?: string;
// Style props
size?: "sm" | "md" | "lg";
variant?: "outline" | "filled";
// DOM props
placeholder?: string;
disabled?: boolean;
readOnly?: boolean;
id?: string;
// Event props
onChange?: (value: string) => void;
onFocus?: () => void;
onBlur?: () => void;
}
function Input(props: InputProps) {
const [formProps, validationProps, styleProps, domProps, eventProps] = splitProps(
props,
["name", "value", "defaultValue", "required"],
["isInvalid", "errorMessage"],
["size", "variant"],
["placeholder", "disabled", "readOnly", "id"],
["onChange", "onFocus", "onBlur"]
);
const inputStyles = computeInputStyles({
...styleProps,
isInvalid: validationProps.isInvalid
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
eventProps.onChange?.(e.target.value);
};
return (
<div className="input-wrapper">
<input
{...domProps}
{...formProps}
style={inputStyles}
onChange={handleChange}
onFocus={eventProps.onFocus}
onBlur={eventProps.onBlur}
aria-invalid={validationProps.isInvalid}
/>
{validationProps.isInvalid && validationProps.errorMessage && (
<span className="input-error">{validationProps.errorMessage}</span>
)}
</div>
);
}
// HOC for prop forwarding
function withForwardedProps<T extends Record<string, any>>(
Component: React.ComponentType<T>,
forwardedPropNames: string[]
) {
return function ForwardedComponent(props: T) {
const [forwardedProps, componentProps] = splitProps(props, forwardedPropNames);
return (
<div {...forwardedProps}>
<Component {...componentProps} />
</div>
);
};
}
// Usage
const DivWithForwardedProps = withForwardedProps(Button, ["id", "className", "data-testid"]);
<DivWithForwardedProps
id="my-button"
className="wrapper"
variant="solid"
onClick={() => console.log("clicked")}
>
Click me
</DivWithForwardedProps>Common TypeScript interfaces and types for React component development.
// Prop getter pattern types
type PropGetter<P = Record<string, unknown>, R = Record<string, unknown>> = (
props?: Merge<DOMAttributes, P>,
ref?: React.Ref<any>
) => R & React.RefAttributes<any>;
type RequiredPropGetter<P = Record<string, unknown>, R = Record<string, unknown>> = (
props: Merge<DOMAttributes, P>,
ref?: React.Ref<any>
) => R & React.RefAttributes<any>;
// Render prop pattern types
type MaybeRenderProp<P = Record<string, unknown>> =
React.ReactNode | ((props: P) => React.ReactNode);
// ARIA and DOM interfaces
interface AriaLabelingProps {
"aria-label"?: string;
"aria-labelledby"?: string;
"aria-describedby"?: string;
}
interface AriaValidationProps {
"aria-invalid"?: boolean;
"aria-required"?: boolean;
"aria-readonly"?: boolean;
"aria-errormessage"?: string;
}
interface IdProps {
id?: string;
}
interface InputDOMEvents {
onFocus?: React.FocusEventHandler;
onBlur?: React.FocusEventHandler;
onChange?: React.ChangeEventHandler;
onKeyDown?: React.KeyboardEventHandler;
onKeyUp?: React.KeyboardEventHandler;
}
interface InputDOMProps extends AriaLabelingProps, AriaValidationProps, IdProps {
placeholder?: string;
disabled?: boolean;
readOnly?: boolean;
required?: boolean;
autoComplete?: string;
autoFocus?: boolean;
}
interface DOMElement extends Element, HTMLOrSVGElement {}
interface DOMAttributes<T = DOMElement> {
id?: string;
className?: string;
style?: React.CSSProperties;
tabIndex?: number;
role?: string;
"data-testid"?: string;
}
interface InputDOMAttributes<T = DOMElement> extends DOMAttributes<T>, InputDOMProps, InputDOMEvents {}Usage Examples:
import {
PropGetter,
MaybeRenderProp,
AriaLabelingProps,
InputDOMAttributes
} from "@chakra-ui/utils";
// Hook using PropGetter pattern
interface UseButtonProps {
isDisabled?: boolean;
onClick?: () => void;
}
interface UseButtonReturn {
buttonProps: PropGetter;
isPressed: boolean;
}
function useButton({ isDisabled, onClick }: UseButtonProps): UseButtonReturn {
const [isPressed, setIsPressed] = React.useState(false);
const buttonProps: PropGetter = (props = {}, ref = null) => ({
...props,
ref,
disabled: isDisabled,
onClick: (event: React.MouseEvent) => {
if (!isDisabled) {
onClick?.();
props.onClick?.(event);
}
},
onMouseDown: (event: React.MouseEvent) => {
setIsPressed(true);
props.onMouseDown?.(event);
},
onMouseUp: (event: React.MouseEvent) => {
setIsPressed(false);
props.onMouseUp?.(event);
},
"aria-pressed": isPressed,
"data-pressed": isPressed
});
return { buttonProps, isPressed };
}
// Component using the hook
function CustomButton({ children, ...props }: UseButtonProps & { children: React.ReactNode }) {
const { buttonProps } = useButton(props);
return (
<button {...buttonProps()}>
{children}
</button>
);
}
// Render prop pattern
interface RenderPropComponentProps<T> {
data: T[];
render: MaybeRenderProp<{ items: T[]; isEmpty: boolean }>;
}
function DataRenderer<T>({ data, render }: RenderPropComponentProps<T>) {
const isEmpty = data.length === 0;
if (typeof render === "function") {
return <>{render({ items: data, isEmpty })}</>;
}
return <>{render}</>;
}
// Usage
<DataRenderer
data={users}
render={({ items, isEmpty }) => (
isEmpty ? (
<p>No users found</p>
) : (
<ul>
{items.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
)
)}
/>
// ARIA-compliant input component
interface AccessibleInputProps extends InputDOMAttributes, AriaLabelingProps {
label?: string;
errorMessage?: string;
isRequired?: boolean;
}
function AccessibleInput({
label,
errorMessage,
isRequired,
...inputProps
}: AccessibleInputProps) {
const inputId = inputProps.id || `input-${React.useId()}`;
const errorId = `${inputId}-error`;
return (
<div>
{label && (
<label htmlFor={inputId}>
{label}
{isRequired && <span aria-label="required">*</span>}
</label>
)}
<input
{...inputProps}
id={inputId}
required={isRequired}
aria-invalid={!!errorMessage}
aria-describedby={errorMessage ? errorId : inputProps["aria-describedby"]}
/>
{errorMessage && (
<span id={errorId} role="alert">
{errorMessage}
</span>
)}
</div>
);
}