React component composition cheatsheet

This is a small list of common techniques used in React to compose and enhance components. Feel free to create Pull Requests with further techniques or fixes.

Basic containment

Elements can be passed as children into components.

const Layout = ({ children, theme }) => (
  <div className={`theme-${theme}`}>
    <main>{children}</main>
  </div>
);

const Content = () => (
  <div>Some fancy content</div>
);

ReactDOM.render(
  <Layout theme='dark'>
    <Content />
  </Layout>
, containerEl);

Containment with multiple slots using children

Children are not limited to being elements. You can pass basically anything, including plain objects.

const Layout = ({ children, theme }) => (
  <div className={`theme-${theme}`}>
    <header>{children.header}</header>
    <main>{children.content}</main>
    <footer>{children.footer}</footer>
  </div>
);

const Header = () => (
  <h5>Header title</h5>
);

const Footer = () => (
  <em>footer note</em>
);

const Content = () => (
  <div>Some fancy content</div>
);

ReactDOM.render(
  <Layout theme='dark'>
    {{
      header: <Header />,
      content: <Content />,
      footer: <Footer />
    }}
  </Layout>
, containerEl);

Containment with multiple slots using props

Elements can also be passed in using props.

const Layout = ({ header, content, footer, theme }) => (
  <div className={`theme-${theme}`}>
    <header>{header}</header>
    <main>{content}</main>
    <footer>{footer}</footer>
  </div>
);

const Header = () => (
  <h5>Header title</h5>
);

const Footer = () => (
  <em>footer note</em>
);

const Content = () => (
  <div>Some fancy content</div>
);

ReactDOM.render(
  <Layout
    theme='dark'
    header={<Header />}
    content={<Content />}
    footer={<Footer />}
  />
, containerEl);

Enhancing contained elements

It's possible to enhance certain elements with additional props using React.cloneElement.

const Layout = ({ children, theme }) => (
  <div className={`theme-${theme}`}>
    <main>{React.cloneElement(children, { theme })}</main>
  </div>
);

const Content = ({ theme }) => (
  <div>Currently using this theme: {theme}</div>
);

ReactDOM.render(
  <Layout theme='dark'>
    <Content />
  </Layout>
, containerEl);

Passing components as props

Like elements, you can also pass components in using props.

const Layout = ({ children, contentComponent, theme }) => {
  const ContentComponent = contentComponent;
  return (
    <div className={`theme-${theme}`}>
      <main><ContentComponent theme={theme} /></main>
    </div>
  );
};

const Content = ({ theme }) => (
  <div>Currently using this theme: {theme}</div>
);

ReactDOM.render(
  <Layout
    theme='dark'
    contentComponent={Content}
  />
, containerEl);

Higher Order Components (HOC)

Higher Order Components are functions which get a component passed in as argument and return a new component.

function createComponentWithDefaultProps(WrappedComponent, defaultProps) {
  return (props) => (
      <WrappedComponent {...defaultProps} {...props} />
  );
}

const Layout = ({ children, theme }) => (
  <div className={`theme-${theme}`}>
    <main>{children}</main>
  </div>
);

const DarkLayout = createComponentWithDefaultProps(Layout, { theme: 'dark' });

ReactDOM.render(
  <DarkLayout>Some content</DarkLayout>
, containerEl);

Using functions as children

Since children can be anything, they can also be functions.

class FetchTheme extends React.Component {

  constructor() {
    super();
    this.state = {
      theme: null
    };
  }

  componentDidMount() {
    fetch('/api/currentTheme')
      .then((res) => res.text())
      .then(((theme) => {
        this.setState({ theme });
      }));
  }

  render() {
    const { children } = this.props;
    const { theme } = this.state;
    return theme ? children(theme) : null;
  }
}

const Layout = ({ children, theme }) => (
  <div className={`theme-${theme}`}>
    <main>{children}</main>
  </div>
);

ReactDOM.render(
  <FetchTheme>
  {
    (theme) => (
      <Layout theme={theme}>Some content</Layout>
    )
  }
  </FetchTheme>
, containerEl);

License

Copyright (c) 2017 Simon Kusterer Licensed under the MIT license.