How to create a masonry layout component using React.

Masonry layout is a kind of layout where the width or height of the elements are fixed while the other dimension is variable. It also ensures a uniform gap between elements.

Masonry layout

In this tutorial, we shall create a React component that renders its children using a uniform width masonry layout like in this pen I created. Below is how we expect the component to be used.

<MasonryLayout>
<div>item</div>
<div>item</div>
// more items
</MasonryLayout>

Prerequisites.

  • Basic javascript knowledge such as declaring variables and working with arrays.
  • A functional React development environment. You can use create-react-app.
  • Basic React — JSX, functional components and propType declaration.
  • prop-types package. npm install --save prop-types

Creating the component.

The component we’re going to create is going to be a functional component.

import React from 'react';
import PropTypes from 'prop-types';
const MasonryLayout = props => {
}

Our component will have some props — columns (number of columns in the masonry layout), gap(space between items) and children. We shall have to do some computations with column and gap, so their types will be number.

Let’s define the propTypes for the component and their default values as shown below.

MasonryLayout.propTypes = {
columns: PropTypes.number.isRequired,
gap: PropTypes.number.isRequired,
children: PropTypes.arrayOf(PropTypes.element),
};
MasonryLayout.defaultProps = {
columns: 2,
gap: 20,
};

The children prop will be an array of elements that will be defined between the start (<MasonryLayout>)and end (</MasonryLayout>) tag of our component. We are going to use this prop to access the items that our layout component will render.

We need to dynamically create arrays that will contain items in each column. Inside the MasonryLayout function, let’s create and declare some variables.

const MasonryLayout = props => {
const columnWrapper = {};
const result = [];
}

The columnWrapper object will enable us to dynamically create arrays that we can use as “buckets” for holding items in each column. The result array will contain column (jsx) elements. This is what shall be returned by the component.

Create the columns as shown below.

// create columns
for (let i = 0; i < props.columns; i++) {
columnWrapper[`column${i}`] = [];
}

Divide the children into columns as shown below.

for (let i = 0; i < props.children.length; i++) {
const columnIndex = i % props.columns;
columnWrapper[`column${columnIndex}`].push(
<div style={{ marginBottom: `${props.gap}px`}}>
{props.children[i]}
</div>
);
}

In the code above, were wrapping the child element with a div before pushing in the column. This enables us to add space between items in the column using bottom margin.

The next step will be wrapping the items in each column with a div and pushing it into the result array as shown below.

for (let i = 0; i < props.columns; i++) {
result.push(
<div
style={{
marginLeft: `${i > 0 ? props.gap : 0}px`,
flex: 1,
}}>
{columnWrapper[`column${i}`]}
</div>
);
}

We need space between the columns, so adding a left margin on all columns other than the first does the trick. The columns will be rendered in a flex container hence the flex: 1.

Let’s wrap the result in a flex container and return it.

return (
<div style={{ display: 'flex' }}>
{result}
</div>
);

Using the component.

Let’s render 12 div elements each with a random height between 200px and 500px.

<MasonryLayout columns={3} gap={25}>
{
[...Array(12).keys()].map(key => {
const height = 200 + Math.ceil(Math.random() * 300);

return (
<div style={{height: `${height}px`}} />
)
})
}
</MasonryLayout>

Congrats, we’re done creating a reusable masonry layout component. If you have any questions, engage me in the comments.


Do you need to hire top developers? Talk to Andela to help you scale
Are you looking to accelerate your career as a developer? Andela is currently hiring senior developers.
Apply now.