Creating a flexbox-based Grid component in React
Tags: react, grid, flexbox, css
Published: 2020-05-18 11:41:53

This post is about how to create a flexbox-based grid component in React. The component should be reusable, easy to configure, and allow us to make the following layouts:

`----` `----` `----` `----`
`------` `------` `------`
`----------` `----------`
`----` `----------------`
`----` `-----------` `----`

### Base React Componets

Let's create the components that we will work with.

```javascript
const Grid = ({ children }) => (
  <div className='grid'>
    {children}
  </div>
);
```

```javascript
const GridRow = ({ children }) => (
  <div className='grid__row'>
    {children}
  </div>
);
```

```javascript
const GridColumn = ({ children }) => (
  <div className='grid__column'>
    {children}
  </div>
);
```

Now I append `GridRow` and `GridColumn` to the `Grid` component as static variables `Grid.Row` and `Grid.Column`.

```javascript
import React from 'react';
import GridRow from './GridRow';
import GridColumn from './GridColumn';

const Grid = ({ children }) => (
  <div className='grid'>
    {children}
  </div>
);

Grid.Row = GridRow;
Grid.Column = GridColumn;

export default Grid;
```

### Base styles

As already mentioned, the grid is based on flexbox, so we need to set the `display` property as 'flex'.

```css
.grid, .grid__row {
  display: flex;
  width: 100%;
}

.grid {
  flex-direction: column;
  flex-wrap: nowrap;
}

.grid__row {
  flex-direction: row;
  flex-wrap: wrap;
}
```

### Columns

In order to make a row with n-columns we need to set the `width` property of the columns. For example, if we want to get a 4-columns row, the value of the column width should be 25%, of 3 columns - 33%, of 2 columns - 50%. Therefore, we need CSS classes for these cases.

```css
.grid__row--columns-4 > [class^="grid__column"] {
  width: 25%;
}

.grid__row--columns-3 > [class^="grid__column"] {
  width: 33%;
}

.grid__row--columns-2 > [class^="grid__column"] {
  width: 50%;
}
```

And let's modify the GridRow component.

```javascript
import cx from 'classnames';

const GridRow = ({ children, columns = 4 }) => {
  const cls = cx('grid__row', {
    [`grid__row--columns-${columns}`]: columns,
  });
  
  return (
    <div className={cls}>
      {children}
    </div>
  );
};
```

And now we can do...

```javascript
<Grid>
  <Grid.Row columns={4}>
    <Grid.Column>1</Grid.Column>
    <Grid.Column>2</Grid.Column>
    <Grid.Column>3</Grid.Column>
    <Grid.Column>4</Grid.Column>
  </Grid.Row>
</Grid>
```
`----` `----` `----` `----`
```javascript
<Grid>
  <Grid.Row columns={3}>
    <Grid.Column>1</Grid.Column>
    <Grid.Column>2</Grid.Column>
    <Grid.Column>3</Grid.Column>
  </Grid.Row>
</Grid>
```
`----` `----` `----`
```javascript
<Grid>
  <Grid.Row columns={2}>
    <Grid.Column>1</Grid.Column>
    <Grid.Column>2</Grid.Column>
  </Grid.Row>
</Grid>
```
`----` `----`

### Columns with different widths

In order to make columns with different widths, we use the 'flex-grow' property.

```css
.grid__column--width-3 {
  flex-grow: 3;
}

.grid__column--width-2 {
  flex-grow: 2;
}
```

```javascript
const GridColumn = ({ children, width }) => {
  const cls = cx('grid__column', {
    [`grid__column--width-${width}`]: width,
  });
...
```

And now we can do such things:

```javascript
<Grid>
  <Grid.Row columns={4}>
    <Grid.Column>1</Grid.Column>
    <Grid.Column width={2}>2</Grid.Column>
    <Grid.Column>3</Grid.Column>
  </Grid.Row>
</Grid>
```
`----` `-----------` `----`
```javascript
<Grid>
  <Grid.Row columns={3}>
    <Grid.Column>1</Grid.Column>
    <Grid.Column width={2}>2</Grid.Column>
  </Grid.Row>
</Grid>
```
`----` `----------------`