Create A Responsive Masonry Layout With Horizontal Flow
Tags: tutorial, javascript, react, beginners
Published: 2020-09-17 13:41:41

###Introduction

I love online photo galleries. You can get lost in photography for hours, particularly when the photographer really has something special to show off. It’s with that in mind that, as a developer and designer, you feel that justice has to be done to those images.

![Screenshot of the gallery](https://dev-to-uploads.s3.amazonaws.com/i/5jnexc4705aks2cxb4gz.png)

And this was my take on the infamous photo gallery project. I’m extremely happy with how it turned out and, although it may not seem much of an endeavour on the surface, it introduced me to a little something known as _Masonry_ layout - and why there are a number of plugins and libraries developed to tackle just this. However, by the time I had decided what I wanted to achieve, I didn’t want to opt for a package to do this for me. I wanted to tackle this myself!

###What is Masonry?

Masonry in web terms is the layout of unevenly sized tile, or brick, like elements in a way that there are only even gaps between them on both the x and y axis. It allows for a _Pinterest_-esque style look to your web page. 

![Representation of the masonry layout vertically](https://dev-to-uploads.s3.amazonaws.com/i/iy9csgk4ix1cnni0q7tf.png)

A quick search brings up multiple tutorials and suggestions from developers aiming to help you achieve this layout, in particular with a CSS only approach. I too really wanted to achieve this effect with only CSS, however there was always one downside to the solutions I found - the content flowed _vertically_. Given that this was a photographers website where content would be updated over time, it made sense to me that the images should flow from left to right, showing the newest uploads at the top of the page.

![Representation of the masonry layout horizontally](https://dev-to-uploads.s3.amazonaws.com/i/9gjdgqbr5ydca0hiac32.png)

As it turned out, I wasn’t able to get what I was looking for using CSS alone without having either fixed height rows or vertical flow, both using flex or grid.

![Representation of a fixed height rows layout](https://dev-to-uploads.s3.amazonaws.com/i/e5zwijxr5s08gkqdklj4.png)


###Tools For The Job

As is common lately I chose React as my working platform. This was simply because I knew I would be using a lot of components when building the rest of this portfolio site and I like how React, utilising `useState` and `useEffect`, makes handling API requests easy. For the CMS I chose Cosmic (formerly CosmicJS), a headless CMS with a very easy to use REST/GraphQL API and user-friendly dashboard for the client. It also provides a seemingly limitless free-tier and Imgix compression abilities. Win win!

So, without further ado, let’s move on from the why and what, to the how. I’m not planning on going into depth on how the entire site was put together - it’s a fairly simple React site using `react-router-dom`. I should also say that this isn’t a tutorial on React, `styled-components`, or any of the other tools I’ve used. We’ll jump right in at the gallery part.


###Getting It Done

I used the following file structure for the gallery part of the project:

![File tree for the project](https://dev-to-uploads.s3.amazonaws.com/i/lk96ilnlyotp1u8c1orl.png)

Let’s start by taking a look at `GalleryPage.js`. This is essentially the wrapper which all of the good stuff sits inside.  From there, we can dig deeper.
 
![Code: React imports](https://dev-to-uploads.s3.amazonaws.com/i/d31jpw3wdt1lv1bqa319.png)

As always we make our start with our imports. As I mentioned at the beginning, the gallery makes use of the `useState` and `useEffect` hooks provided natively by React, so we bring those in along with React itself. We then import `styled-components` which, for anyone who is unfamiliar with it, is a fantastic library for utilising JS-in-CSS. Bear in mind, you won’t need styled-components to make this work - you can simply import normal CSS files or S/CSS modules into your project.

I generally like to structure my imports as above, with native or `npm` provided imports at the top, logic/hooks/etc imports just after that, and lastly any components. It’s not gospel of course, it’s just the way I like to keep myself organised.

I’ll come back to the rest of the imports soon. For now, let’s take a look at the rest of `GalleryPage`.

![Code: React state and fetch function](https://dev-to-uploads.s3.amazonaws.com/i/32vnl1itmg5776s5vxxc.png)

There’s quite a lot going on there, so let’s break it down. First, we declare the component name which, in this instance, is `GalleryPage`. We then declare a number of variables which will be used to hold our site’s state.

`isLoading` and `setIsLoading` will, unsurprisingly, manage our loading sequence whilst we wait for fetch to retrieve our data, and `imageData` and `setImageData` will look after our data once it is retrieved. We’ll come back to the others shortly.

Using JavaScript’s `fetch` API we then make a request to the CosmicJS endpoint, for which you will need to get yourself an API key, and we receive back a JSON object. We can access the array we need under the object’s `media` key, so make sure that this is what you set as your state. Once we’ve set our state to our array using `setImageData(data.media)` we will now be able to use it to generate our images.

In the return statement for our component we need the following code:

![Code: React component return statement and contents](https://dev-to-uploads.s3.amazonaws.com/i/z6jvuyy9fj25sw2moo9p.png)

The `GalleryWrapper` and `GalleryWrapper` elements have been created using `styled-components`, for now just think of them as any other React component. Inside here we `map` through our array and, ultimately, create our images. Now, the astute of you may have noticed something off in the code snippet above. We originally stored our array in the `useState` variable `imageData`, so why now are we trying to access something from `sortedImageData`?

This is where the important pieces of the puzzle come into play. Earlier we imported two additional modules - `useWindowSize`, which is a custom hook, and `sortEveryNth`, which is a JS function.

![Code: React function imports](https://dev-to-uploads.s3.amazonaws.com/i/39mca13nmjk9rexyvykj.png)

We’ll start by taking a look at the `useWindowSize` hook which is a custom hook with one important job to do - to listen out for any changes in our browser size and store that result in a state variable.

![Code: useWindowSize react hook](https://dev-to-uploads.s3.amazonaws.com/i/adisjz8adpe792ynrqx3.png)

The hook makes use of both the native `useLayoutEffect` and `useState` hooks, and essentially adds an event listener to the window which fires each time a `resize` event occurs. This is the stored, and returned, as the constant size.  To have a look into `useLayoutEffect` I recommend the [docs](https://reactjs.org/docs/hooks-reference.html#uselayouteffect).

Now we’ll take a look at the `sortEveryNth` function and see how this all fits together.

![Code: sortEveryNth function](https://dev-to-uploads.s3.amazonaws.com/i/cqzzchlopzexlilqkpjp.png)

This function takes in two parameters, an array (the one that we want sorted) and a single number.  This single number will reflect two things - the amount of columns we want to have in our gallery layout and, subsequently, the iteration count for the sorting algorithm.

![Code: useWindowSize import](https://dev-to-uploads.s3.amazonaws.com/i/3bs3d1h7sjx726uvzqu7.png)

![Code: useEffect hook for handling state changes relating to the window event listener](https://dev-to-uploads.s3.amazonaws.com/i/1bskghgmyo1rjdejj4pt.png)

The `width` we are using within this `useEffect` hook is the value that is being returned from the `useWindowSize` we just created, and from this value it is calculating whether to change state at some arbitrary break points - in this case 1366px, 1024px, and 800px. 

Let’s say for example our browser width increases to, or is initially set at, 1400px. This fits into the first condition of the `if` statement, being greater than 1366px. `setSortedImageData` now calls the `sortEveryNth` function and passes in the `imageData` array to the first parameter, and the value of 4 as the second. The function now begins its work sorting the objects in the array by their index and returning a new array.

![A representation of how the function works on each element in the array](https://dev-to-uploads.s3.amazonaws.com/i/gcd7pw7vlnaohdfra9cn.png)

As this image hopefully explains well, the function skips through each item by n, which is in this case 4, and pushes the object into the new array. Once this is complete, the new array, imaginatively named `newArr`, is returned back to `setSortedImageData` and consequently stored into `sortedImageData`. And, after all of that, this is where we are mapping our data from, creating our `GalleryImage` components and appending them onto  `GalleryWrapper`. The `useEffect` hook has both width and `imageData` in its dependency array, and these are responsible for ensuring everything is re-rendered once any changes occur to the browser size.

That’s essentially all of the heavy lifting out of the way. The last part to be put in place, to make sure everything works, is the CSS. I found that the use of `column-count` gave both the best and most predictable results.

![Code: A styled-component showing the required CSS](https://dev-to-uploads.s3.amazonaws.com/i/qw9c0lx15p2cj5cjzq23.png)

It’s important to use media queries at the same breakpoints as you set in the `useEffect` hook as these will work in unison to both lay out the page and calculate the sorting correctly. As you can see I actually started this desktop first - not intentionally, it was just how it happened.  And, as I mentioned before, any CSS will work here so don’t get hung up on how this looks outside of the CSS.

And that’s it! I hope I was able to share something interesting with you here, and I’d really appreciate any feedback on either the content or the writing. This is my first ever post, and I’d like to do this more often and making it worthwhile would be a massive bonus.

You can check out anything related to me or this project on my [website](https://martinwheelerweb.com)