Ojasa Mirai

Ojasa Mirai

ReactJS

Loading...

Learning Level

🟢 Beginner🔵 Advanced
🎁 What are Props?📤 Passing Props to Components🔍 Reading Props in Components🎯 Props for Customization✅ Prop Types & Validation🔄 Props vs State⬇️ Passing Functions as Props🚀 Building Reusable Components
Reactjs/Props/Reusable Components

🚀 Enterprise Component Design — Building Scalable Component Systems

Enterprise applications require component systems that scale across teams, remain maintainable as requirements evolve, and enforce consistency. Advanced design patterns create these powerful abstractions.


🎯 Component Library Architecture and Versioning

Component libraries need clear contracts, semantic versioning, and backward compatibility. Structure determines how teams consume and evolve components.

Enterprise components operate as published APIs. Changes must follow semantic versioning. Major versions introduce breaking changes, minor versions add features, patches fix bugs. This stability enables teams to upgrade on their own schedule.

// Version 1.0.0 - initial release
interface ButtonV1Props {
  label: string;
  onClick: () => void;
  disabled?: boolean;
}

export const Button: React.FC<ButtonV1Props> = ({
  label,
  onClick,
  disabled,
}) => (
  <button onClick={onClick} disabled={disabled}>
    {label}
  </button>
);

// Version 1.1.0 - add new feature (backward compatible)
interface ButtonV1_1Props extends ButtonV1Props {
  variant?: "primary" | "secondary";
}

// Can't change existing interface, must extend
// Existing consumers still work without changes
export const ButtonV1_1: React.FC<ButtonV1_1Props> = ({
  label,
  onClick,
  disabled,
  variant = "primary",
}) => {
  const className = `btn btn-${variant}`;
  return (
    <button onClick={onClick} disabled={disabled} className={className}>
      {label}
    </button>
  );
};

// Version 2.0.0 - breaking changes (new major version)
interface ButtonV2Props {
  children: React.ReactNode; // Changed: children instead of label
  onPress: () => void; // Changed: renamed onClick
  disabled?: boolean;
  variant?: "primary" | "secondary" | "ghost"; // Added option
  size?: "sm" | "md" | "lg"; // New feature
}

// Consumers must update imports when upgrading major versions
export const ButtonV2: React.FC<ButtonV2Props> = ({
  children,
  onPress,
  disabled,
  variant = "primary",
  size = "md",
}) => {
  const classNames = `btn btn-${variant} btn-${size}`;
  return (
    <button onClick={onPress} disabled={disabled} className={classNames}>
      {children}
    </button>
  );
};

// Export current version as default
export { ButtonV2 as Button };

<details>

<summary>📚 More Examples</summary>

// Example 2: Deprecation pattern for gradual migration
import { useEffect } from "react";

// Old API - mark as deprecated
interface DeprecatedCardProps {
  title: string;
  content: string;
  bgColor?: string; // Deprecated in v2
}

function DeprecatedCard({
  title,
  content,
  bgColor,
}: DeprecatedCardProps) {
  useEffect(() => {
    if (bgColor) {
      console.warn(
        "bgColor prop is deprecated. Use variant prop instead. Will be removed in v3.0.0"
      );
    }
  }, [bgColor]);

  const variant = bgColor === "blue" ? "primary" : "default";

  return (
    <div className={`card card-${variant}`}>
      <h3>{title}</h3>
      <p>{content}</p>
    </div>
  );
}

// New API
interface CardProps {
  title: string;
  children: React.ReactNode;
  variant?: "default" | "primary" | "success";
}

function Card({ title, children, variant = "default" }: CardProps) {
  return (
    <div className={`card card-${variant}`}>
      <h3>{title}</h3>
      {children}
    </div>
  );
}

// Migration helper
function migrateDeprecatedCard(
  props: DeprecatedCardProps
): CardProps {
  return {
    title: props.title,
    children: props.content,
    variant: props.bgColor === "blue" ? "primary" : "default",
  };
}

</details>

💡 Composition Over Inheritance Patterns

Enterprise components rarely inherit. Instead, composition through props enables flexibility while avoiding tight coupling and inheritance complexity.

Composition is more flexible than inheritance. Components accept behavior through props rather than extending base classes. This enables mixing behaviors without the constraints of inheritance hierarchies.

// Don't do this: inheritance-based component hierarchy
abstract class BaseButton extends React.Component {
  abstract handleClick(): void;
  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}

class PrimaryButton extends BaseButton {
  handleClick() {
    console.log("Primary clicked");
  }
}

// Do this: composition-based, flexible system
interface ButtonComposition {
  variant?: "primary" | "secondary";
  size?: "sm" | "md" | "lg";
  icon?: React.ComponentType<{ className: string }>;
  loading?: boolean;
  onClick?: () => void;
}

function Button({
  variant = "primary",
  size = "md",
  icon: Icon,
  loading = false,
  onClick,
  children,
}: ButtonComposition & { children: React.ReactNode }) {
  return (
    <button
      onClick={onClick}
      disabled={loading}
      className={`btn btn-${variant} btn-${size}`}
    >
      {Icon && <Icon className="mr-2" />}
      {loading ? "Loading..." : children}
    </button>
  );
}

// Reuse with different compositions
<Button variant="primary" size="lg" onClick={() => {}}>
  Save
</Button>

<Button variant="secondary" icon={DeleteIcon} onClick={() => {}}>
  Delete
</Button>

// Create specific button types without inheritance
function PrimaryButton(props: ButtonComposition & { children: React.ReactNode }) {
  return <Button variant="primary" {...props} />;
}

function DangerButton(props: ButtonComposition & { children: React.ReactNode }) {
  return <Button variant="danger" {...props} />;
}

<details>

<summary>📚 More Examples</summary>

// Example 2: Composable layout components
interface FlexProps {
  direction?: "row" | "column";
  gap?: "xs" | "sm" | "md" | "lg";
  align?: "start" | "center" | "end" | "stretch";
  justify?: "start" | "center" | "end" | "between" | "around";
  wrap?: boolean;
  children: React.ReactNode;
}

function Flex({
  direction = "row",
  gap = "md",
  align = "stretch",
  justify = "start",
  wrap = false,
  children,
}: FlexProps) {
  const gapClasses = { xs: "gap-1", sm: "gap-2", md: "gap-4", lg: "gap-8" };
  const alignClasses = { start: "items-start", center: "items-center", end: "items-end", stretch: "items-stretch" };
  const justifyClasses = { start: "justify-start", center: "justify-center", end: "justify-end", between: "justify-between", around: "justify-around" };

  return (
    <div
      className={`flex flex-${direction} ${gapClasses[gap]} ${alignClasses[align]} ${justifyClasses[justify]} ${wrap ? "flex-wrap" : ""}`}
    >
      {children}
    </div>
  );
}

// Compose into domain-specific components
function FormRow({ children }: { children: React.ReactNode }) {
  return (
    <Flex direction="column" gap="sm">
      {children}
    </Flex>
  );
}

function HorizontalButtonGroup({ children }: { children: React.ReactNode }) {
  return (
    <Flex direction="row" gap="md" justify="end">
      {children}
    </Flex>
  );
}

</details>

🔧 Component Documentation and Storybook

Enterprise components need living documentation. Storybook serves as both documentation and development environment.

// Card component with Storybook stories
import type { Meta, StoryObj } from "@storybook/react";

export interface CardProps {
  title: string;
  subtitle?: string;
  children: React.ReactNode;
  variant?: "default" | "elevated" | "outlined";
  interactive?: boolean;
  onClick?: () => void;
}

export function Card({
  title,
  subtitle,
  children,
  variant = "default",
  interactive = false,
  onClick,
}: CardProps) {
  return (
    <div
      className={`card card-${variant} ${interactive ? "interactive" : ""}`}
      onClick={interactive ? onClick : undefined}
    >
      <h3>{title}</h3>
      {subtitle && <p className="subtitle">{subtitle}</p>}
      <div className="content">{children}</div>
    </div>
  );
}

// Storybook metadata
const meta: Meta<typeof Card> = {
  title: "Components/Card",
  component: Card,
  parameters: {
    layout: "padded",
  },
  tags: ["autodocs"],
  argTypes: {
    variant: {
      options: ["default", "elevated", "outlined"],
      control: { type: "radio" },
    },
    interactive: {
      control: { type: "boolean" },
    },
  },
};

export default meta;
type Story = StoryObj<typeof meta>;

// Basic story
export const Default: Story = {
  args: {
    title: "Card Title",
    children: "This is the card content",
  },
};

// Variant stories
export const Elevated: Story = {
  args: {
    title: "Elevated Card",
    variant: "elevated",
    children: "Elevated variant with shadow",
  },
};

export const Interactive: Story = {
  args: {
    title: "Interactive Card",
    interactive: true,
    children: "Click me!",
    onClick: () => alert("Clicked!"),
  },
};

// Edge cases
export const LongContent: Story = {
  args: {
    title: "Card with Long Content",
    children:
      "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
  },
};

export const WithSubtitle: Story = {
  args: {
    title: "Main Title",
    subtitle: "Subtitle text",
    children: "Content goes here",
  },
};

<details>

<summary>📚 More Examples</summary>

// Example 2: Component accessibility documentation
export function AccessibleButton({
  children,
  ariaLabel,
  ariaDescribedBy,
  disabled,
  onClick,
}: {
  children: React.ReactNode;
  ariaLabel?: string;
  ariaDescribedBy?: string;
  disabled?: boolean;
  onClick?: () => void;
}) {
  return (
    <button
      aria-label={ariaLabel}
      aria-describedby={ariaDescribedBy}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

// Accessibility story
export const AccessibilityStory: Story = {
  args: {
    children: "Save",
    ariaLabel: "Save document",
    ariaDescribedBy: "save-help",
  },
  render: (args) => (
    <>
      <AccessibleButton {...args} />
      <p id="save-help">Saves the current document to the server</p>
    </>
  ),
};

</details>

🎨 Testing Enterprise Components

Enterprise components need comprehensive testing. Unit tests verify behavior, integration tests verify prop communication, visual tests catch regressions.

import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";

describe("Button Component", () => {
  // Props validation
  test("renders with provided label", () => {
    render(<Button onClick={() => {}}>Click me</Button>);
    expect(screen.getByRole("button")).toHaveTextContent("Click me");
  });

  // Event handling
  test("calls onClick handler", () => {
    const onClick = jest.fn();
    render(<Button onClick={onClick}>Click</Button>);
    fireEvent.click(screen.getByRole("button"));
    expect(onClick).toHaveBeenCalledTimes(1);
  });

  // Disabled state
  test("disables button when disabled prop is true", () => {
    render(
      <Button onClick={() => {}} disabled>
        Click
      </Button>
    );
    expect(screen.getByRole("button")).toBeDisabled();
  });

  // Variants
  test("applies variant class", () => {
    render(
      <Button variant="primary" onClick={() {}}>
        Click
      </Button>
    );
    expect(screen.getByRole("button")).toHaveClass("btn-primary");
  });

  // Loading state
  test("shows loading state", () => {
    render(
      <Button loading onClick={() {}}>
        Click
      </Button>
    );
    expect(screen.getByRole("button")).toHaveTextContent("Loading...");
  });

  // Accessibility
  test("has accessible label", () => {
    render(
      <Button ariaLabel="Save document" onClick={() {}}>
        Save
      </Button>
    );
    expect(screen.getByRole("button")).toHaveAttribute(
      "aria-label",
      "Save document"
    );
  });

  // Composition with icon
  test("renders icon when provided", () => {
    const Icon = () => <span>📌</span>;
    render(
      <Button icon={Icon} onClick={() {}}>
        Save
      </Button>
    );
    expect(screen.getByText("📌")).toBeInTheDocument();
  });
});

🔑 Key Takeaways

  • ✅ Enterprise components follow semantic versioning for stability
  • ✅ Composition patterns are more flexible than inheritance
  • ✅ Use Storybook to document and test components visually
  • ✅ Test component behavior, accessibility, and edge cases
  • ✅ Props interfaces form the contract between components and consumers
  • ✅ Design for extensibility without modification (Open/Closed Principle)
  • ✅ Consider backward compatibility for widely-used components
  • ✅ Document patterns and anti-patterns for consuming teams

Ready to practice? Challenges | Take the Quiz


Resources

Python Docs

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

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn