
ReactJS
Advanced customization patterns enable building component systems that remain flexible and maintainable as requirements grow. Polymorphic components, variant systems, and CSS-in-JS integration allow components to adapt to any context.
Polymorphic components accept an `as` prop that determines which HTML element or component they render as. This pattern enables maximum flexibility while maintaining type safety.
The `as` prop pattern allows a single component to render as different HTML elements while maintaining full TypeScript type safety for the underlying element's props. This is powerful for building generic layout components that work anywhere.
import { ElementType, ComponentPropsWithoutRef, forwardRef } from "react";
interface PolymorphicProps<
E extends ElementType = ElementType
> {
as?: E;
children?: React.ReactNode;
className?: string;
}
type PolymorphicComponentProps<
E extends ElementType,
P = {}
> = PolymorphicProps<E> & Omit<ComponentPropsWithoutRef<E>, keyof PolymorphicProps<E>> & P;
interface BoxProps {
variant?: "default" | "card" | "section";
padding?: "sm" | "md" | "lg";
}
const Box = forwardRef<
HTMLDivElement,
PolymorphicComponentProps<"div", BoxProps>
>(
(
{
as: Component = "div",
className = "",
variant = "default",
padding = "md",
...rest
},
ref
) => {
const paddingClasses = {
sm: "p-2",
md: "p-4",
lg: "p-8",
};
const variantClasses = {
default: "bg-white",
card: "bg-white rounded-lg shadow-md",
section: "bg-gray-50 border",
};
return (
<Component
ref={ref}
className={`${variantClasses[variant]} ${paddingClasses[padding]} ${className}`}
{...rest}
/>
);
}
);
// Usage: renders as different elements with correct prop types
<Box as="div" variant="card" padding="lg">
Content
</Box>
<Box as="section" variant="section">
Section content
</Box>
<Box as="article" className="custom">
Article content
</Box><details>
<summary>📚 More Examples</summary>
// Example 2: Button polymorphic component
interface ButtonProps {
variant?: "primary" | "secondary" | "ghost";
size?: "sm" | "md" | "lg";
isLoading?: boolean;
}
const Button = forwardRef<
HTMLButtonElement,
PolymorphicComponentProps<"button", ButtonProps>
>(
(
{
as: Component = "button",
variant = "primary",
size = "md",
isLoading = false,
children,
disabled,
...rest
},
ref
) => {
const variantClasses = {
primary: "bg-blue-600 text-white",
secondary: "bg-gray-200 text-gray-900",
ghost: "bg-transparent text-gray-600",
};
const sizeClasses = {
sm: "px-2 py-1 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg",
};
return (
<Component
ref={ref}
className={`${variantClasses[variant]} ${sizeClasses[size]} rounded cursor-pointer disabled:opacity-50`}
disabled={disabled || isLoading}
{...rest}
>
{isLoading ? "Loading..." : children}
</Component>
);
}
);
// Can render as button or link while maintaining type safety
<Button onClick={() => {}}>Button</Button>
<Button as="a" href="/home">
Link
</Button></details>
Variant systems use props to control styling through predefined combinations. CVA and similar libraries create type-safe, maintainable variant definitions.
Variant systems decouple styling logic from component behavior. By defining all valid combinations upfront, you ensure consistency across your UI and make it impossible to create invalid combinations.
import { cva, type VariantProps } from "class-variance-authority";
// Define all button variants upfront
const buttonVariants = cva(
"inline-flex items-center justify-center rounded font-medium transition-colors",
{
variants: {
variant: {
primary: "bg-blue-600 text-white hover:bg-blue-700",
secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
danger: "bg-red-600 text-white hover:bg-red-700",
ghost: "text-gray-600 hover:bg-gray-100",
},
size: {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-base",
lg: "h-12 px-6 text-lg",
},
state: {
default: "",
disabled: "opacity-50 cursor-not-allowed",
loading: "opacity-75 cursor-wait",
},
},
compoundVariants: [
{
variant: "ghost",
state: "disabled",
className: "opacity-30",
},
],
defaultVariants: {
variant: "primary",
size: "md",
state: "default",
},
}
);
// Extract type from variants
type ButtonVariants = VariantProps<typeof buttonVariants>;
interface ButtonProps
extends ButtonVariants,
React.ButtonHTMLAttributes<HTMLButtonElement> {}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, state, ...rest }, ref) => (
<button
ref={ref}
className={buttonVariants({ variant, size, state, className })}
{...rest}
/>
)
);
// Type-safe variant combinations
<Button variant="primary" size="lg">
Save
</Button>
<Button variant="danger" size="sm" state="loading">
Deleting...
</Button>
// Compound variants ensure consistency
<Button variant="ghost" state="disabled">
Disabled ghost button
</Button><details>
<summary>📚 More Examples</summary>
// Example 2: Card component with CVA
const cardVariants = cva("rounded border", {
variants: {
variant: {
default: "bg-white border-gray-200",
elevated: "bg-white shadow-lg",
outlined: "bg-white border-2 border-gray-300",
},
interactive: {
true: "cursor-pointer hover:shadow-md transition-shadow",
false: "",
},
padding: {
none: "p-0",
sm: "p-4",
md: "p-6",
lg: "p-8",
},
},
compoundVariants: [
{
variant: "elevated",
interactive: true,
className: "hover:shadow-xl",
},
],
defaultVariants: {
variant: "default",
interactive: false,
padding: "md",
},
});
type CardVariants = VariantProps<typeof cardVariants>;
interface CardProps
extends CardVariants,
React.HTMLAttributes<HTMLDivElement> {}
const Card = forwardRef<HTMLDivElement, CardProps>(
({ className, variant, interactive, padding, ...rest }, ref) => (
<div
ref={ref}
className={cardVariants({ variant, interactive, padding, className })}
{...rest}
/>
)
);</details>
Building extensible components requires designing props that can be extended without modification. This enables plugins, themes, and custom behaviors.
// Base component props
interface BaseComponentProps {
children?: React.ReactNode;
className?: string;
data?: Record<string, any>;
}
// Plugin system for extensibility
interface ComponentPlugin<T = any> {
name: string;
enhance?: (props: T) => Partial<T>;
render?: (component: React.ReactNode) => React.ReactNode;
}
interface ExtensibleComponentProps<P = {}> extends BaseComponentProps {
plugins?: ComponentPlugin<P>[];
}
function withPlugins<P extends ExtensibleComponentProps>(
Component: React.ComponentType<P>
) {
return function PluginComponent({
plugins = [],
...props
}: P) {
// Apply plugin enhancements
let enhancedProps = { ...props };
plugins.forEach((plugin) => {
if (plugin.enhance) {
enhancedProps = { ...enhancedProps, ...plugin.enhance(enhancedProps) };
}
});
// Render component
let rendered = <Component {...(enhancedProps as P)} />;
// Apply plugin renders
plugins.forEach((plugin) => {
if (plugin.render) {
rendered = plugin.render(rendered);
}
});
return rendered;
};
}
// Example usage with plugins
const plugins: ComponentPlugin[] = [
{
name: "tooltip",
render: (component) => (
<div title="Help text">{component}</div>
),
},
{
name: "analytics",
enhance: (props) => ({
onClick: () => {
console.log("Tracked click");
},
}),
},
];
<Component plugins={plugins} />;<details>
<summary>📚 More Examples</summary>
// Example 2: Theme-aware component system
interface ThemeConfig {
colors: Record<string, string>;
spacing: Record<string, string>;
typography: Record<string, string>;
}
const ThemeContext = React.createContext<ThemeConfig>({
colors: {},
spacing: {},
typography: {},
});
interface ThemedComponentProps extends BaseComponentProps {
colorScheme?: "primary" | "secondary" | "accent";
size?: "compact" | "normal" | "spacious";
}
function ThemedComponent({
colorScheme = "primary",
size = "normal",
className,
...props
}: ThemedComponentProps) {
const theme = React.useContext(ThemeContext);
const themeClassName = `color-${colorScheme} size-${size}`;
return (
<div className={`${themeClassName} ${className}`} {...props} />
);
}
// Customize theme at any level
<ThemeContext.Provider value={customTheme}>
<ThemedComponent colorScheme="primary" size="spacious" />
</ThemeContext.Provider></details>
Composing complex props from smaller, reusable prop builders creates maintainable, scalable UI systems.
// Props builder pattern for complex components
interface DialogProps {
title: string;
content: React.ReactNode;
actions: Array<{
label: string;
onClick: () => void;
variant?: "primary" | "secondary" | "danger";
}>;
size?: "sm" | "md" | "lg";
onClose: () => void;
}
// Builder for common dialog patterns
const dialogBuilders = {
confirm: (message: string) => ({
content: message,
actions: [
{ label: "Cancel", variant: "secondary" as const },
{ label: "Confirm", variant: "primary" as const },
],
}),
alert: (message: string) => ({
content: message,
actions: [{ label: "OK", variant: "primary" as const }],
}),
delete: (itemName: string) => ({
content: `Are you sure you want to delete "${itemName}"? This cannot be undone.`,
actions: [
{ label: "Cancel", variant: "secondary" as const },
{ label: "Delete", variant: "danger" as const },
],
}),
};
// Usage: clean, composable dialog creation
const dialogProps: DialogProps = {
title: "Confirm Action",
...dialogBuilders.confirm("Do you want to proceed?"),
onClose: () => {},
};
<Dialog {...dialogProps} />;Ready to practice? Challenges | Next: Props Validation
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward