DEV Community

Cover image for Create a reusable table with react, styled-components and compound components design pattern
Joseph Mukorivo
Joseph Mukorivo

Posted on

Create a reusable table with react, styled-components and compound components design pattern

So I have been using react for about 10 months now after switching from a framework I thought I will never leave, you guessed it 🤗 Angular. Angular code was clean and readable and I loved it because it was strongly typed by default (😊 of course that's the beauty of TypeScript).

When my colleague introduced me to React, I said to my self how can this guy introduce me to such a mess, writing jsx was a bit weird to me at first but trust me once you start writing jsx you will never go back.

I started looking for design patterns to make my react code clean and reusable. During this journey, I came across compound components and I started using them with a CSS-in-JS library (styled-components), and I have to say this😎 I was in love😍. My code looked clean and it was also easy to debug.

If you have used a native HTML <select> and <option> you can easily understand the concept behind compound components.

<select>
  <option value="value1">key1</option>
  <option value="value2">key2</option>
  <option value="value3">key3</option>
</select>
Enter fullscreen mode Exit fullscreen mode

If you try to use one without the other it would not work, and also it doesn't make sense.
Now, let's get a look at our React <Table /> component that exposes a compound component to understand these principles further. Here is how it looks like.

<Table>
  <Table.Head>
    <Table.TR>
      <Table.TH>Heading 1</Table.TH>
      <Table.TH>Heading 2</Table.TH>
    </Table.TR>
  </Table.Head>
  <Table.Body>
    <Table.TR>
      <Table.TH>data 1</Table.TH>
      <Table.TH>data 2</Table.TH>
    </Table.TR>
    <Table.TR>
      <Table.TH>data 3</Table.TH>
      <Table.TH>data 4</Table.TH>
    </Table.TR>
  </Table.Body>
</Table>
Enter fullscreen mode Exit fullscreen mode

But before we get to that, this is how I structure my components. Let me know if you have a better way of structuring components I would love to try it out.

📦components
 ┣ 📂table
   ┣ 📂styles
   ┃ ┗ 📜table.js
   ┗ 📜index.js

Enter fullscreen mode Exit fullscreen mode

All my styles will be in the styles directory and index.js file imports the styled components from the styles directory. Below is how I will style my table. We are ignoring css however just to keep the post short.

import styled from 'styled-components';

export const StyledTable = styled.table`
  // custom css goes here
`;

export const THead = styled.thead`
 // custom css goes here
`;

export const TFoot = styled.tfoot`
  // custom css goes here
`;

export const TBody = styled.tbody`
 // custom css goes here
`;

export const TR = styled.tr`
  // custom css goes here
`;

export const TH = styled.th`
  // custom css goes here
`;

export const TD = styled.td`
  // custom css goes here
`;
Enter fullscreen mode Exit fullscreen mode

Now in the index.js that's where all the action is. Remember with our table component we are exporting just the table component and the other components we accessing them from the table component using the dot notation.

import { StyledTable, THead, TBody, TFoot, TH, TR, TD } from './styles/table';

export const Table = ({ children, ...rest }) => {
  return <StyledTable {...rest}>{children}</StyledTable>;
};

Table.Head = ({ children, ...rest }) => {
  return <THead {...rest}>{children}</THead>;
};

Table.Body = ({ children, ...rest }) => {
  return <TBody {...rest}>{children}</TBody>;
};

Table.Foot = ({ children, ...rest }) => {
  return <TFoot {...rest}>{children}</TFoot>;
};

Table.TH = ({ children, ...rest }) => {
  return <TH {...rest}>{children}</TH>;
};

Table.TR = ({ children, ...rest }) => {
  return <TR {...rest}>{children}</TR>;
};

Table.TD = ({ children, ...rest }) => {
  return <TD {...rest}>{children}</TD>;
};
Enter fullscreen mode Exit fullscreen mode

I know I have to explain some things here like how we are accessing these other components when we are not directly exporting them and how the children prop works.

The only component that we are exporting here is the <Table/> component which wraps the <StyledTable/> component. We then use the dot notation to attach other components to the <Table/> component. If we were using class components we will use the static keyword to do the same thing. We can now for example access the styled table row like so <Table.TR/>

Anything passed between the opening and closing tags of a component can be accessed using the children prop in react, for example, if we write this code <Table.TR>data</Table.TR>, props.children will be equal to 'data'. That's basically how the children prop works.

We want the end-users of our components to be able to customize them so we are taking everything they are passing as props and spread it on the styled component using the object destructuring syntax {..rest}.

I hope this post helped you understand compound components. Feel free to comment on areas you need clarity I will respond, or areas you think I need to improve. In the future, we will create a dropdown component using this pattern but now there will be state and we will be using custom hooks and the Context API to manage the state of the dropdown.

Discussion (2)

Collapse
rconr007 profile image
rconr007

Cool. Thank you. New to the React world. Might take you up on that. Great article.

Collapse
josemukorivo profile image
Joseph Mukorivo Author

Glad it helped, I am thinking of sharing my folder structure before I get into some of these concepts. Its one of the areas I struggled with when I got started with react.