dev.log / syntax diaries

Practical code notes, tools, and guided learning for developers.

Practical guides, developer tools, and tutorials for modern web developers, with the same focused tone across writing, utilities, and learning tracks.

BlogToolsTutorialsAboutContactAdmin Login
Privacy PolicyTerms of ServiceCookie Policy

© 2026 The Syntax Diaries · System_Operational

The Syntax Diaries logoThe Syntax Diaries
BlogToolsTutorialsAbout
build log live
Tutorial / React
Components30 minbeginner

Props and PropTypes

Learn how to pass data between components using props and validate them with PropTypes for robust, reusable components.

On This Page

What are Props?Props in ActionWays to Receive Props1. Props Object (Traditional Way)2. Destructuring Props (Modern Way)3. Destructuring with Default ValuesTypes of Props1. String Props2. Number Props3. Boolean Props4. Array Props5. Object Props6. Function Props (Event Handlers)Advanced Props Patterns1. Rest Props and Spread2. Children Prop3. Render Props Pattern4. Compound ComponentsPropTypes for Type CheckingInstalling PropTypesBasic PropTypesAdvanced PropTypesComplex PropTypes ExamplesReal-World Examples1. Complete User Profile Component2. Flexible Button ComponentCommon Props Mistakes and Solutions1. Mutating Props2. Not Validating Required Props3. Inefficient Prop PassingBest Practices1. Use Descriptive Prop Names2. Group Related Props into Objects3. Use Default Props WiselySummaryWhat's Next?

Props and PropTypes#

Props (short for "properties") are how you pass data from parent components to child components in React. They're the primary way to make components reusable and dynamic. Think of props as function arguments for your components.

What are Props?#

Props are read-only data passed from a parent component to a child component. They allow you to customize how a component behaves and what it displays.

Props in Action#

// Parent component passing props
function App() {
  return (
    <div>
      <Greeting name="Alice" age={25} />
      <Greeting name="Bob" age={30} />
      <Greeting name="Charlie" age={35} />
    </div>
  );
}

// Child component receiving props
function Greeting(props) {
  return (
    <div>
      <h1>Hello, {props.name}!</h1>
      <p>You are {props.age} years old.</p>
    </div>
  );
}

Ways to Receive Props#

1. Props Object (Traditional Way)#

function UserCard(props) {
  return (
    <div className="user-card">
      <img src={props.avatar} alt={props.name} />
      <h2>{props.name}</h2>
      <p>{props.email}</p>
      <span className="role">{props.role}</span>
    </div>
  );
}

// Usage
<UserCard 
  name="Sarah Johnson"
  email="sarah@example.com"
  avatar="sarah.jpg"
  role="Frontend Developer"
/>

2. Destructuring Props (Modern Way)#

function UserCard({ name, email, avatar, role }) {
  return (
    <div className="user-card">
      <img src={avatar} alt={name} />
      <h2>{name}</h2>
      <p>{email}</p>
      <span className="role">{role}</span>
    </div>
  );
}

3. Destructuring with Default Values#

function UserCard({ 
  name, 
  email, 
  avatar = '/default-avatar.png',  // Default value
  role = 'User',                   // Default value
  isOnline = false                 // Default value
}) {
  return (
    <div className="user-card">
      <div className="avatar-container">
        <img src={avatar} alt={name} />
        {isOnline && <div className="online-indicator" />}
      </div>
      <h2>{name}</h2>
      <p>{email}</p>
      <span className="role">{role}</span>
    </div>
  );
}

// Usage - some props will use defaults
<UserCard 
  name="John Doe"
  email="john@example.com"
  isOnline={true}
  // avatar and role will use default values
/>

Types of Props#

1. String Props#

function Button({ text, variant }) {
  return (
    <button className={`btn btn-\${variant}`}>
      {text}
    </button>
  );
}

// Usage
<Button text="Click me" variant="primary" />
<Button text="Cancel" variant="secondary" />

2. Number Props#

function ProgressBar({ progress, max = 100 }) {
  const percentage = (progress / max) * 100;
  
  return (
    <div className="progress-bar">
      <div 
        className="progress-fill"
        style={{ width: `\${percentage}%` }}
      />
      <span className="progress-text">
        {progress}/{max} ({Math.round(percentage)}%)
      </span>
    </div>
  );
}

// Usage
<ProgressBar progress={75} max={100} />
<ProgressBar progress={3} max={10} />

3. Boolean Props#

function Alert({ message, isVisible, isError = false, isDismissible = true }) {
  if (!isVisible) return null;
  
  return (
    <div className={`alert \${isError ? 'alert-error' : 'alert-info'}`}>
      <span>{message}</span>
      {isDismissible && (
        <button className="alert-close">×</button>
      )}
    </div>
  );
}

// Usage
<Alert 
  message="Success! Your data has been saved." 
  isVisible={true} 
  isError={false}
/>

<Alert 
  message="Error! Something went wrong." 
  isVisible={true} 
  isError={true}
  isDismissible={false}
/>

4. Array Props#

function TagList({ tags, colorScheme = 'blue' }) {
  return (
    <div className="tag-list">
      {tags.map((tag, index) => (
        <span 
          key={index}
          className={`tag tag-\${colorScheme}`}
        >
          {tag}
        </span>
      ))}
    </div>
  );
}

// Usage
<TagList 
  tags={['React', 'JavaScript', 'Frontend']} 
  colorScheme="green"
/>

5. Object Props#

function ProductCard({ product }) {
  return (
    <div className="product-card">
      <img src={product.image} alt={product.name} />
      <h3>{product.name}</h3>
      <p className="price">\${product.price}</p>
      <p className="description">{product.description}</p>
      
      <div className="product-meta">
        <span className="category">{product.category}</span>
        <span className="rating">★ {product.rating}</span>
      </div>
      
      <button className="add-to-cart">
        Add to Cart
      </button>
    </div>
  );
}

// Usage
const product = {
  id: 1,
  name: "Wireless Headphones",
  price: 99.99,
  description: "High-quality wireless headphones with noise cancellation",
  image: "headphones.jpg",
  category: "Electronics",
  rating: 4.5
};

<ProductCard product={product} />

6. Function Props (Event Handlers)#

function Modal({ isOpen, title, children, onClose, onConfirm }) {
  if (!isOpen) return null;
  
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content" onClick={(e) => e.stopPropagation()}>
        <div className="modal-header">
          <h2>{title}</h2>
          <button onClick={onClose} className="close-btn">×</button>
        </div>
        
        <div className="modal-body">
          {children}
        </div>
        
        <div className="modal-footer">
          <button onClick={onClose} className="btn-secondary">
            Cancel
          </button>
          <button onClick={onConfirm} className="btn-primary">
            Confirm
          </button>
        </div>
      </div>
    </div>
  );
}

// Usage
function App() {
  const [showModal, setShowModal] = useState(false);
  
  const handleConfirm = () => {
    console.log('User confirmed!');
    setShowModal(false);
  };
  
  return (
    <div>
      <button onClick={() => setShowModal(true)}>
        Open Modal
      </button>
      
      <Modal
        isOpen={showModal}
        title="Confirm Action"
        onClose={() => setShowModal(false)}
        onConfirm={handleConfirm}
      >
        <p>Are you sure you want to proceed?</p>
      </Modal>
    </div>
  );
}

Advanced Props Patterns#

1. Rest Props and Spread#

function CustomInput({ label, error, ...inputProps }) {
  return (
    <div className="form-field">
      {label && <label className="form-label">{label}</label>}
      <input 
        className={`form-input \${error ? 'form-input-error' : ''}`}
        {...inputProps}  // Spread remaining props to input
      />
      {error && <span className="form-error">{error}</span>}
    </div>
  );
}

// Usage - all props except label and error go to input
<CustomInput
  label="Email Address"
  type="email"
  placeholder="Enter your email"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
  required
  autoComplete="email"
/>

2. Children Prop#

The children prop is special - it represents content between component tags:

function Card({ title, children, footer }) {
  return (
    <div className="card">
      {title && (
        <div className="card-header">
          <h3>{title}</h3>
        </div>
      )}
      
      <div className="card-body">
        {children}  {/* Content between <Card></Card> tags */}
      </div>
      
      {footer && (
        <div className="card-footer">
          {footer}
        </div>
      )}
    </div>
  );
}

// Usage
<Card title="User Profile" footer={<button>Edit</button>}>
  <img src="avatar.jpg" alt="User" />
  <h4>John Doe</h4>
  <p>Software Engineer</p>
  <p>john@example.com</p>
</Card>

3. Render Props Pattern#

function DataFetcher({ url, children }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);
      })
      .catch(error => {
        setError(error.message);
        setLoading(false);
      });
  }, [url]);
  
  // Call children as a function with data
  return children({ data, loading, error });
}

// Usage
<DataFetcher url="/api/users">
  {({ data, loading, error }) => {
    if (loading) return <div>Loading users...</div>;
    if (error) return <div>Error: {error}</div>;
    
    return (
      <ul>
        {data.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    );
  }}
</DataFetcher>

4. Compound Components#

function Tabs({ children, activeTab, onTabChange }) {
  return (
    <div className="tabs">
      {children}
    </div>
  );
}

function TabList({ children }) {
  return (
    <div className="tab-list" role="tablist">
      {children}
    </div>
  );
}

function Tab({ children, value, isActive, onClick }) {
  return (
    <button
      className={`tab \${isActive ? 'tab-active' : ''}`}
      onClick={() => onClick(value)}
      role="tab"
    >
      {children}
    </button>
  );
}

function TabPanels({ children }) {
  return (
    <div className="tab-panels">
      {children}
    </div>
  );
}

function TabPanel({ children, value, activeTab }) {
  if (value !== activeTab) return null;
  
  return (
    <div className="tab-panel" role="tabpanel">
      {children}
    </div>
  );
}

// Usage
function App() {
  const [activeTab, setActiveTab] = useState('profile');
  
  return (
    <Tabs activeTab={activeTab} onTabChange={setActiveTab}>
      <TabList>
        <Tab value="profile" isActive={activeTab === 'profile'} onClick={setActiveTab}>
          Profile
        </Tab>
        <Tab value="settings" isActive={activeTab === 'settings'} onClick={setActiveTab}>
          Settings
        </Tab>
        <Tab value="billing" isActive={activeTab === 'billing'} onClick={setActiveTab}>
          Billing
        </Tab>
      </TabList>
      
      <TabPanels>
        <TabPanel value="profile" activeTab={activeTab}>
          <h2>User Profile</h2>
          <p>Profile content goes here</p>
        </TabPanel>
        <TabPanel value="settings" activeTab={activeTab}>
          <h2>Settings</h2>
          <p>Settings content goes here</p>
        </TabPanel>
        <TabPanel value="billing" activeTab={activeTab}>
          <h2>Billing</h2>
          <p>Billing content goes here</p>
        </TabPanel>
      </TabPanels>
    </Tabs>
  );
}

PropTypes for Type Checking#

PropTypes help you catch bugs by validating the types of props your components receive:

Installing PropTypes#

npm install prop-types

Basic PropTypes#

import PropTypes from 'prop-types';

function UserCard({ name, age, email, isActive, hobbies, avatar }) {
  return (
    <div className="user-card">
      <img src={avatar} alt={name} />
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>Email: {email}</p>
      <p>Status: {isActive ? 'Active' : 'Inactive'}</p>
      <div>
        Hobbies: {hobbies.join(', ')}
      </div>
    </div>
  );
}

UserCard.propTypes = {
  name: PropTypes.string.isRequired,    // Required string
  age: PropTypes.number.isRequired,     // Required number
  email: PropTypes.string.isRequired,   // Required string
  isActive: PropTypes.bool,             // Optional boolean
  hobbies: PropTypes.array,             // Optional array
  avatar: PropTypes.string              // Optional string
};

UserCard.defaultProps = {
  isActive: false,
  hobbies: [],
  avatar: '/default-avatar.png'
};

Advanced PropTypes#

import PropTypes from 'prop-types';

function ProductCard({ product, onAddToCart, tags, size, customStyle }) {
  return (
    <div className={`product-card product-card-\${size}`} style={customStyle}>
      <img src={product.image} alt={product.name} />
      <h3>{product.name}</h3>
      <p>\${product.price}</p>
      <div className="tags">
        {tags.map(tag => (
          <span key={tag} className="tag">{tag}</span>
        ))}
      </div>
      <button onClick={() => onAddToCart(product.id)}>
        Add to Cart
      </button>
    </div>
  );
}

ProductCard.propTypes = {
  // Object with specific shape
  product: PropTypes.shape({
    id: PropTypes.number.isRequired,
    name: PropTypes.string.isRequired,
    price: PropTypes.number.isRequired,
    image: PropTypes.string.isRequired
  }).isRequired,
  
  // Function
  onAddToCart: PropTypes.func.isRequired,
  
  // Array of specific type
  tags: PropTypes.arrayOf(PropTypes.string),
  
  // One of specific values
  size: PropTypes.oneOf(['small', 'medium', 'large']),
  
  // Object (CSS styles)
  customStyle: PropTypes.object,
  
  // Any type
  metadata: PropTypes.any,
  
  // Node (anything that can be rendered)
  icon: PropTypes.node,
  
  // Element (React element)
  header: PropTypes.element,
  
  // Instance of a class
  date: PropTypes.instanceOf(Date),
  
  // Custom validator
  priority: function(props, propName, componentName) {
    if (props[propName] && (props[propName] < 1 || props[propName] > 10)) {
      return new Error(
        `Invalid prop `\${propName}` of value `\${props[propName]}` supplied to `\${componentName}`, expected a number between 1 and 10.`
      );
    }
  }
};

ProductCard.defaultProps = {
  tags: [],
  size: 'medium',
  customStyle: {}
};

Complex PropTypes Examples#

import PropTypes from 'prop-types';

function DataTable({ columns, data, onRowClick, sortable, pagination }) {
  // Component implementation...
  return <div>/* Table implementation */</div>;
}

DataTable.propTypes = {
  // Array of objects with specific shape
  columns: PropTypes.arrayOf(
    PropTypes.shape({
      key: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired,
      sortable: PropTypes.bool,
      render: PropTypes.func
    })
  ).isRequired,
  
  // Array of any objects
  data: PropTypes.arrayOf(PropTypes.object).isRequired,
  
  // Function with specific signature
  onRowClick: PropTypes.func,
  
  // Object with optional properties
  sortable: PropTypes.shape({
    enabled: PropTypes.bool,
    defaultSort: PropTypes.string,
    direction: PropTypes.oneOf(['asc', 'desc'])
  }),
  
  // Union type - either boolean or object
  pagination: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.shape({
      pageSize: PropTypes.number,
      currentPage: PropTypes.number,
      totalItems: PropTypes.number
    })
  ])
};

DataTable.defaultProps = {
  sortable: {
    enabled: true,
    direction: 'asc'
  },
  pagination: false
};

Real-World Examples#

1. Complete User Profile Component#

import PropTypes from 'prop-types';

function UserProfile({ 
  user, 
  onEdit, 
  onMessage, 
  onFollow, 
  isFollowing, 
  showActions = true,
  compact = false 
}) {
  const {
    id,
    name,
    username,
    avatar,
    bio,
    location,
    website,
    joinDate,
    stats,
    isVerified,
    isOnline
  } = user;

  return (
    <div className={`user-profile \${compact ? 'user-profile-compact' : ''}`}>
      <div className="user-header">
        <div className="avatar-container">
          <img src={avatar} alt={`\${name}'s avatar`} className="avatar" />
          {isOnline && <div className="online-indicator" />}
        </div>
        
        <div className="user-info">
          <div className="name-section">
            <h1 className="name">{name}</h1>
            {isVerified && <span className="verified-badge">✓</span>}
          </div>
          <p className="username">@{username}</p>
          
          {!compact && (
            <>
              {bio && <p className="bio">{bio}</p>}
              
              <div className="metadata">
                {location && (
                  <span className="location">📍 {location}</span>
                )}
                {website && (
                  <a href={website} className="website" target="_blank" rel="noopener noreferrer">
                    🔗 {website}
                  </a>
                )}
                <span className="join-date">
                  📅 Joined {new Date(joinDate).toLocaleDateString()}
                </span>
              </div>
            </>
          )}
        </div>
      </div>

      {!compact && stats && (
        <div className="user-stats">
          <div className="stat">
            <span className="stat-number">{stats.posts.toLocaleString()}</span>
            <span className="stat-label">Posts</span>
          </div>
          <div className="stat">
            <span className="stat-number">{stats.followers.toLocaleString()}</span>
            <span className="stat-label">Followers</span>
          </div>
          <div className="stat">
            <span className="stat-number">{stats.following.toLocaleString()}</span>
            <span className="stat-label">Following</span>
          </div>
        </div>
      )}

      {showActions && (
        <div className="user-actions">
          <button onClick={() => onEdit(id)} className="btn btn-secondary">
            Edit Profile
          </button>
          <button onClick={() => onMessage(id)} className="btn btn-primary">
            Message
          </button>
          <button 
            onClick={() => onFollow(id)} 
            className={`btn \${isFollowing ? 'btn-outline' : 'btn-primary'}`}
          >
            {isFollowing ? 'Unfollow' : 'Follow'}
          </button>
        </div>
      )}
    </div>
  );
}

UserProfile.propTypes = {
  user: PropTypes.shape({
    id: PropTypes.string.isRequired,
    name: PropTypes.string.isRequired,
    username: PropTypes.string.isRequired,
    avatar: PropTypes.string.isRequired,
    bio: PropTypes.string,
    location: PropTypes.string,
    website: PropTypes.string,
    joinDate: PropTypes.string.isRequired,
    isVerified: PropTypes.bool,
    isOnline: PropTypes.bool,
    stats: PropTypes.shape({
      posts: PropTypes.number,
      followers: PropTypes.number,
      following: PropTypes.number
    })
  }).isRequired,
  onEdit: PropTypes.func.isRequired,
  onMessage: PropTypes.func.isRequired,
  onFollow: PropTypes.func.isRequired,
  isFollowing: PropTypes.bool,
  showActions: PropTypes.bool,
  compact: PropTypes.bool
};

UserProfile.defaultProps = {
  isFollowing: false,
  showActions: true,
  compact: false
};

2. Flexible Button Component#

import PropTypes from 'prop-types';

function Button({ 
  children,
  variant = 'primary',
  size = 'medium',
  disabled = false,
  loading = false,
  fullWidth = false,
  icon,
  iconPosition = 'left',
  onClick,
  ...props 
}) {
  const baseClasses = 'btn';
  const variantClasses = `btn-\${variant}`;
  const sizeClasses = `btn-\${size}`;
  const stateClasses = [
    disabled && 'btn-disabled',
    loading && 'btn-loading',
    fullWidth && 'btn-full-width'
  ].filter(Boolean).join(' ');

  const buttonClasses = [baseClasses, variantClasses, sizeClasses, stateClasses]
    .join(' ')
    .trim();

  return (
    <button
      className={buttonClasses}
      disabled={disabled || loading}
      onClick={onClick}
      {...props}
    >
      {loading && <span className="btn-spinner" />}
      
      {icon && iconPosition === 'left' && (
        <span className="btn-icon btn-icon-left">{icon}</span>
      )}
      
      <span className="btn-text">{children}</span>
      
      {icon && iconPosition === 'right' && (
        <span className="btn-icon btn-icon-right">{icon}</span>
      )}
    </button>
  );
}

Button.propTypes = {
  children: PropTypes.node.isRequired,
  variant: PropTypes.oneOf([
    'primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'
  ]),
  size: PropTypes.oneOf(['small', 'medium', 'large']),
  disabled: PropTypes.bool,
  loading: PropTypes.bool,
  fullWidth: PropTypes.bool,
  icon: PropTypes.node,
  iconPosition: PropTypes.oneOf(['left', 'right']),
  onClick: PropTypes.func
};

// Usage examples
<Button variant="primary" size="large" onClick={handleSubmit}>
  Submit Form
</Button>

<Button 
  variant="danger" 
  icon={<TrashIcon />} 
  iconPosition="left"
  onClick={handleDelete}
>
  Delete Item
</Button>

<Button 
  variant="secondary" 
  loading={isLoading} 
  disabled={!isValid}
  fullWidth
>
  {isLoading ? 'Processing...' : 'Save Changes'}
</Button>

Common Props Mistakes and Solutions#

1. Mutating Props#

❌ Wrong - Never mutate props:

function UserList({ users }) {
  users.push({ id: 999, name: 'New User' }); // NEVER DO THIS
  
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

✅ Correct - Create new arrays/objects:

function UserList({ users, showNewUser = false }) {
  const displayUsers = showNewUser 
    ? [...users, { id: 999, name: 'New User' }]  // Create new array
    : users;
  
  return (
    <ul>
      {displayUsers.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

2. Not Validating Required Props#

❌ Wrong - No validation:

function UserCard({ user }) {
  return (
    <div>
      <h2>{user.name}</h2>  {/* Will crash if user is undefined */}
      <p>{user.email}</p>
    </div>
  );
}

✅ Correct - Validate and provide defaults:

import PropTypes from 'prop-types';

function UserCard({ user }) {
  if (!user) {
    return <div>No user data available</div>;
  }
  
  return (
    <div>
      <h2>{user.name || 'Unknown User'}</h2>
      <p>{user.email || 'No email provided'}</p>
    </div>
  );
}

UserCard.propTypes = {
  user: PropTypes.shape({
    name: PropTypes.string,
    email: PropTypes.string
  }).isRequired
};

3. Inefficient Prop Passing#

❌ Wrong - Prop drilling:

function App() {
  const user = { name: 'John', email: 'john@example.com' };
  
  return <Dashboard user={user} />;
}

function Dashboard({ user }) {
  return <Sidebar user={user} />;
}

function Sidebar({ user }) {
  return <UserMenu user={user} />;
}

function UserMenu({ user }) {
  return <div>{user.name}</div>;
}

✅ Better - Component composition:

function App() {
  const user = { name: 'John', email: 'john@example.com' };
  
  return (
    <Dashboard>
      <Sidebar>
        <UserMenu user={user} />
      </Sidebar>
    </Dashboard>
  );
}

function Dashboard({ children }) {
  return <div className="dashboard">{children}</div>;
}

function Sidebar({ children }) {
  return <div className="sidebar">{children}</div>;
}

Best Practices#

1. Use Descriptive Prop Names#

❌ Bad:

<Button type="1" click={handleClick} txt="Submit" />

✅ Good:

<Button variant="primary" onClick={handleClick} children="Submit" />

2. Group Related Props into Objects#

❌ Bad - Too many props:

<UserCard 
  userName="John"
  userEmail="john@example.com" 
  userAvatar="john.jpg"
  userAge={30}
  userLocation="NYC"
/>

✅ Good - Grouped props:

<UserCard user={{
  name: "John",
  email: "john@example.com",
  avatar: "john.jpg",
  age: 30,
  location: "NYC"
}} />

3. Use Default Props Wisely#

✅ Good practices:

function Avatar({ src, alt, size = 'medium', shape = 'circle' }) {
  return (
    <img 
      src={src || '/default-avatar.png'}
      alt={alt}
      className={`avatar avatar-\${size} avatar-\${shape}`}
    />
  );
}

Avatar.propTypes = {
  src: PropTypes.string,
  alt: PropTypes.string.isRequired,  // Required for accessibility
  size: PropTypes.oneOf(['small', 'medium', 'large']),
  shape: PropTypes.oneOf(['circle', 'square'])
};

Summary#

Props are the foundation of component reusability in React:

✅ Props pass data from parent to child components
✅ Use destructuring for cleaner code
✅ Provide default values for optional props
✅ Validate props with PropTypes in development
✅ Never mutate props - they are read-only
✅ Use composition to avoid prop drilling

What's Next?#

In the next lesson, we'll explore Component Composition - advanced patterns for building complex UIs by combining simple components. You'll learn:

  • Higher-order components (HOCs)
  • Render props patterns
  • Compound components
  • Composition vs inheritance
  • Building flexible component APIs

Props enable data flow, but composition enables architectural flexibility!

Previous

Functional Components

Next

Your First React Application