Dennis Gaebel Design technologist and author that loves open source, CSS Architecture, SVG, typography, motion, interaction, and pattern-based design.

CSS Grid: A guide to getting started

15 min read 4320

CSS Grid: A Guide to Getting Started

Editor’s note: This CSS grid guide was last updated on 10 January 2023 to include more information on the differences between CSS grid and CSS flexbox, interactive code examples, and more in-depth definitions of when to use CSS grid. Check out our CSS archive to learn more about what you can do with CSS.

The web is based on and made up of layouts. The way you want your web apps to look across different platforms and devices — mobile, tablets, laptops, and desktops — is determined, to a large extent, by the layout structure.

For example, we have come to embrace a responsive and mobile-first design. This is because most people access the web from their mobile phones.

To achieve this layout initially, developers had to rely on CSS hacks for web apps, like tables, floats, positioning, and inline-blocks. Then, the CSS grid layout was introduced to help developers create better layouts without the hacks — particularly avoiding using floats and tables. What’s more about CSS grid? It has support across all of the different browsers.

With CSS grid, we can create amazing layouts and create responsive web pages seamlessly. Herein lies the power and relevance of the CSS grid layout system: being able to use it to design layouts for mobile-first web apps and responsive websites that cut across different browsers. This article will take a deep dive into CSS grid and show you how to get started in using it.

Jump ahead:

What is CSS grid used for?

CSS grid is used for creating two-dimensional layouts. It is different from CSS flexbox, which can only create one-dimensional layouts and is mainly created for alignments. Being a two-dimensional layout system means that CSS grid allows us to simultaneously work with columns and rows to build complex and responsive layouts. The advent of CSS grid means we no longer need to deploy hacks like positioning and floats.

CSS grid is a great tool when we need to account for the position, sizes, and layers of different elements and how they relate with each other open a webpage. It also makes it easy to rapidly create different types of layouts for websites.

CSS grid vs. CSS flexbox: Which should you use?

As with all things web and software development, the answer to this question is: it depends. While neither CSS grid nor CSS flexbox is a one-size-fits-all solution, there are some instances where CSS grid shines as the right and better tool for the job.

For websites that are composed of elements with varying sizes that need complex layouts like masonry layouts, CSS grid is your best option. While there are flexbox hacks you can implement to achieve these layouts, these hacks are often complex and require longer lines of code because you are not using the right tool for the job.

In cases where you want fine-grained control over the position of each individual element on the page, CSS grid shines as the better tool. CSS grid’s grid-template-areas, grid lines, and grid-spanning features make this possible. While we can also achieve this fine-grained control with flexbox, it usually requires more lines of code, which can be tricky to maintain and manage over time, and wild CSS files are always difficult to work with.

Lastly, in cases where you want greater flexibility to move elements around, regardless of the structure of the HTML markup, CSS grid should be your first choice.

Basic CSS grid principles

At the most basic level, a CSS grid is a two-dimensional layout system for the web. With CSS grid, you can lay content out in rows and columns.

Before we go deeper, let’s take a look at the building block of CSS grid, the display: grid; container.

First, let’s create the HTML elements we want to style with CSS grid:

<body class="container">
    <div class="item box1"><p>One</p></div>
    <div class="item box2"><p>Two</p></div>
    <div class="item box3"><p>Three</p></div>
</body>

Next, we define the grid layout by setting the display to grid:

// define or create a grid 
.container{
    display: grid;
}

CSS grid terminology

Once your grid is defined, you’re in a good position to start. However, that doesn’t do anything magical just yet. We need to tell the grid how big the columns and rows should be. To become better acquainted with the details, let’s discuss the terminology of CSS grid:

  • Grid container: The parent container where you’ve defined your grid display
  • Grid items: The direct children of your grid container
  • Gap: Used to create gutters, the spaces between grid cells, through rows and columns
  • Grid cell: The space between two adjacent rows and two adjacent column grid lines. It’s a single unit of the grid. Here’s a picture to explain a grid cell:

CSS Grid Cell Example

  • Grid area: One or more grid cells make up a rectangular area on the grid. Grid areas must be rectangular in nature. Here’s an example:

CSS Grid Area Example

  • Grid track: Tracks are the space between two adjacent grid lines — essentially, the lines that start and stop rows or columns, so you’ll always have one more than the number of columns and rows you already have. Check it out below:

CSS Grid Track Example

  • Grid lines: These are the dividing lines that are created when you define a grid display:

CSS Grid Lines Example

Implicit and explicit grid lines

Implicit grid lines are visualized as dotted lines in your dev tools and are created by the browser without explicitly defining columns or rows.

Let’s use this code snippet to explain implicit grid lines:

.container{
    display: grid;
    grid-template-columns: 100px 100px 100px;
}

In the code snippet above, we explicitly define what the columns will be. We do not define the rows. So, the browser will decide what it will do to the extra items that aren’t covered by the explicit definition. It does this by creating new rows and moving these extra items into the rows. And that’s implicit!

Explicit grid lines are the solid lines visible in the dev tools for rows and columns defined within your CSS. Explicit scenarios aren’t decisions made by the browser; they’re the decisions you make, which are explicit.

Here’s an interactive example:

See the Pen
CSS Grid – gapless
by Emadamerho Nefe (@nefejames)
on CodePen.

Using gutters to separate grid cells

Gutters are the spaces between grid cells. You can create a gutter using the gap shorthand property, or you can do so explicitly using column-gap and row-gap. Should you use the gap shorthand property to create the gutter, the value you assign to the gap will target both the row and column of the grid.

To provide your grid with gutters, using gap, you’ll define this property in the parent container:

// using gap to create gutter of 20px row and column
.container {
    gap: 20px; /* row and column gap */
}

// this can also be written as
.container {
    column-gap: 20px; 
    row-gap: 20px;
}

See the Pen
CSS Grid – with gap
by Emadamerho Nefe (@nefejames)
on CodePen.

Fractional units

The fr value, otherwise known as the fractional unit, solves the problem of automatically distributing free space among elements and replaces the need for percentages.

Let’s update our example’s CSS file to switch from pixels to fr:

.container {
  display: grid;
  gap: 10px;
  grid-template-columns: 1fr 1fr 1fr;
}

Note how the sizes of the child elements in the grid automatically increase in width to fill the available space in the grid parent:

See the Pen
CSS Grid – 1fr
by Emadamerho Nefe (@nefejames)
on CodePen.

Creating CSS grid containers, columns, and rows

Seeing as nothing really happens until you actually define rows and columns, we should now learn to create them. There are various ways to define each, so let’s begin by learning how to create columns.

Creating columns and column spanning

The grid-template-columns property is created in the parent-container where each value passed represents the number of explicit columns. For instance:

.parent-container {
  grid-template-columns: 20px 1fr 40px;
}

The example above tells the browser to create three columns. The first column is 20px wide, the second column is 1fr, and the third column is 40px. You can also pass functions and keywords as values, which we’ll address later in this article.



The technique known as spanning allows child items to stretch across columns present in your grid by defining column start and column end positions, as shown below:

.container{
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
}

.child-item:nth-child(1) {
  grid-column: span 2; /* span 2 columns */
}

.child-item:nth-child(2) {
  grid-column: span 3
}

The snippet above defines each column’s start and end position. However, you can also include a slash between two values.

You can also achieve this same result using the code below. The difference here is that the first value before the / will target the start line of the column, while the second value will target the end of the grid:

.child-item:nth-child(1) {
  grid-column: span 2 / 5;
}

The code above tells the browser to span my grid item two columns wide and end it at column five. This represents the column-start/column-end position, but notice we didn’t tell the browser where to start the column; we only said where to end.

This is something the browser figures out. It automatically understands that we want to span two columns and end column five’s grid line, so it calculates that the item will start at the end of column two’s grid line.

If we want to tell the browser exactly where to start a child-item, we’d remove the span keyword:

.child-item {
  grid-column: 2 / 5;
}

The snippet tells the browser to start my item at column 2 and end at column 5. Here’s an example:

See the Pen
CSS Grid – grid column span
by Emadamerho Nefe (@nefejames)
on CodePen.

Spanning the full width

There’s this sneaky -1 value that you’ll frequently use to span the full grid container, and it’s all because your grid is one more than the number of columns/rows defined. It really has to do with tracks. This track officially ends at the end of your grid container:

.child-item {
  grid-column: 1 / -1;
}

This example starts at the first column and spans the full width of the defined grid, regardless of the number of columns defined in your CSS. Cool, right? Heck yeah, it is! Check it out below:

See the Pen
CSS Grid – full width -1
by Emadamerho Nefe (@nefejames)
on CodePen.

Creating and spanning rows

Grid rows are just like columns, only they run horizontally instead of vertically. The property is written identically to our column counterpart, except we use the word rows instead. Here’s how that’s written in CSS:

.parent-container {
  grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
}

The above example defines four explicit rows, each of which is 1fr high. If any children wrap to a new row, that’s when your implicit rows will be created.

With the grid-row shorthand property, we have the same opportunity to span rows just like we can span columns, like so:

.item:nth-child(1){
  grid-row: span 2
}

.item:nth-child(2){
  grid-row: span 3
}

See the Pen
CSS Grid – grid template rows
by Emadamerho Nefe (@nefejames)
on CodePen.

A shorter way to create rows and columns

The grid-template() shorthand lets you define a start and end position for rows and columns in one line and is comparatively far more accessible to read than the grid property we previously discussed. I personally dig this shorthand. It just makes sense to me.

Here’s an example:

.parent-container {
  grid-template: 1fr 1fr / 100px 100px 100px;
}

The shorthands we’re provided with above are grid-template-rows and grid-template-columns, but we also have grid-template-areas to consider, too — more on that shortly.

The first value is the start position, and the second value is the end position, followed by the slash / that repeats this pattern. You can think of it like this: the part in front of the/ is your rows, and the part behind the / are your columns:

See the Pen
CSS Grid – grid-template
by Emadamerho Nefe (@nefejames)
on CodePen.

Controlling column and row overflow

What can be done when you have that pesky overflow lingering around in your grid? You throw your computer out the window, right? Wrong! You can control the overflow of your grid in one swift action using some carefully placed properties for column and row overflow. Let’s start by investigating column overflow.

Column overflow

The grid-auto-columns property will help handle your overflow needs. It’s defined on the parent-container and is written in the following fashion:

.parent-container {
    grid-auto-columns: 50px;
}

The above code snippet implies that when an implicit grid is created, the size of the column should take up two fractional units of the parent-container:

.parent-container {
    grid-auto-columns: 50px; /* overflow size */
    grid-auto-flow: column; /* overflow type */
}

The result of the code above tells the overflow to create columns that are 150px in size, meaning that the implicit overflow will be in the form of columns, not rows:

See the Pen
CSS Grid – auto column overflow
by Emadamerho Nefe (@nefejames)
on CodePen.

Row overflow

This property works exactly like our column overflow property and is written the same way, too — bonus!

.parent-container {
    grid-auto-rows: 150px;
}

Just like the property we used for columns, we dictate the size of our implicit rows by passing a value of row to grid-auto-flow. What we get are rows that are created any time an overflow is present:

.parent-container {
    grid-auto-rows: 150px;
    grid-auto-flow: row;
}

With the above lines, we define the overflow type (rows) and the overflow size (150px) of the implicit overflow for children elements of the grid container:

See the Pen
CSS Grid – auto row overflow
by Emadamerho Nefe (@nefejames)
on CodePen.

Building grid templates

Grid templates are nice to use when you don’t know how many columns you’ll need upfront. They’re also great to use with media queries to achieve some rearranging magic. Here’s how you write the property:

.parent-container {
    grid-template-areas: "sidebar1 content sidebar2";
}

Using a set of quotes containing words of your choosing, such as "sidebar1 content sidebar2", we begin to define our grid areas. You can also define multiple areas. For example:

.container {
  display: grid;
  gap: 10px;
  grid-template:
    "header header header" auto
    "sidebar1 content sidebar2" 1fr
    "footer footer footer" auto
}

This property alleviates the need to worry about line numbers, and each one used will be positioned accordingly. You set the position by defining the property on a child item of your choosing:

.header{
  grid-area: header;
}

.sidebar1{
  grid-area: sidebar1;
}

.content{
    grid-area: content;
}

.sidebar2{
  grid-area: sidebar2;
}

.footer{
    grid-area: footer;
}

Using grid-area, we pass the corresponding area as a value. Nothing more, nothing less; it’s that straightforward. Here’s an example:

See the Pen
CSS Grid – grid template areas
by Emadamerho Nefe (@nefejames)
on CodePen.

Named grid lines

This particular feature of CSS grid, named grid lines, might be useful if you want to explicitly direct your layout behavior. You are granted the ability to name positions on your grid instead of using nameless numerical values:

CSS Grid Naming Lines Example

Let’s see how this works. First, the HTML:

  <body>
    <div class="container">
      <div class="item header">Header</div>
      <div class="item content">Content</div>
      <div class="item articles">Articles</div>
      <div class="item footer">Footer</div>
      <div class="item ads">Ads</div>
    </div>
  </body>

Then, the CSS:

.container {
  display: grid;
  grid-template-columns: [start sidebar-start] 3fr [sidebar-end content-start] 5fr [content-end ads-start] 3fr [end ads-end];
  grid-template-rows: [start header-start] auto [header-end content-start] auto [content-end footer-start] auto [end footer-end];
}

The region is given a name followed by the size (constraint). To define the position, we turn to children, where we’ll define the value based on the named regions above:

.header {
  grid-column: start / end;
  grid-row: start / header-end;
}
.articles {
  grid-column: start / sidebar-end;
  grid-row: header-end / footer-start;
}
.content {
  grid-column: content-start / content-end;
  grid-row: content-start / content-end;
}
.footer {
  grid-column: start / end;
  grid-row: footer-start / footer-end;
}
.ads {
  grid-column: ads-start / end;
  grid-row: header-end / footer-start;
}

See the Pen
CSS Grid: named grid lines
by Emadamerho Nefe (@nefejames)
on CodePen.

Using functions with CSS grid

These handy little devils are the helping hands for your grid desires. They’re the superstars that help make your code-writing life easier and far more efficient. Let’s start with one of my favorites: repeat().

repeat()

The repeat() function allows recurring patterns to be written succinctly — one you’ll use quite a bit when it comes to CSS grid. We define how many rows or columns we desire followed by a constraint, but you can also pass other functions or keywords as arguments. See the code below:

.parent-container {
    grid-template-columns: repeat(2, 1fr);
}

See the Pen
CSS Grid – repeat first example
by Emadamerho Nefe (@nefejames)
on CodePen.

Here’s an example of repeat() in action to create four columns that are 1fr wide, but you can also create repeating patterns:

.parent-container {
    grid-template-columns: repeat(2, 50px 1fr) 2fr;
}

In this example, the pattern repeats twice. The first column is 50px wide, and the next column is 1fr. The pattern is repeated once more and concludes with the final column set to 2fr:

See the Pen
CSS Grid – repeat second example
by Emadamerho Nefe (@nefejames)
on CodePen.

fit-content()

The fit-content() property does one thing: clamps a value to a given size passed:

fit-content(100px)

You’ll mostly use it with grid-template-columns and grid-template-row. For example:

.parent-container {
    grid-template-columns: fit-content(200px) fit-content(400px) 1fr;
}

This creates three columns with a max width of 200px and 400px, and 1fr respectively. You can also think of it as another way to define a max-width for grid columns and rows. Here’s another example:

See the Pen
CSS Grid – fit-content()
by Emadamerho Nefe (@nefejames)
on CodePen.

minmax()

The minmax() function defines a range between a set of values that functions as — you guessed it — a minimum and maximum constraint — and writing it is a breeze:

minmax(20px, 100px)

When content breaks column bounds, this function can be used inside repeat(). For example:

.parent-container {
    grid-template-columns: repeat(3, minmax(150px, 1fr))
}

If you want your children to wrap like champs, this is the way to go — especially when more elements will be added dynamically:

See the Pen
CSS Grid – minmax()
by Emadamerho Nefe (@nefejames)
on CodePen.

Keywords

Keywords are extremely valuable to your arsenal, so you’ll wanna get familiar with them. Let’s start with the ones you’ll most likely use and certainly the most confusing: auto-fit and auto-fill.

auto-fill / auto-fit

The auto-fill keyword ends the grid on the explicit grid line and fills all the available space. It also stretches items inside the grid’s tracks to fit. This makes it a worthy approach for wrapping elements when combined with minmax().

The auto-fit keyword ends the grid at the explicit column line. This is the opposite of what auto-fill does, as it expands the explicit grid.

You can use this keyword within the repeat() function. For example:

repeat(auto-fill, 150px)

Since the above doesn’t explicitly state how many columns are desired, extra implicit grids or columns will be added. This is the equivalent of telling the browser to just figure it out. And it chops the grid into as many spaces as possible that fill up the available space. You can read more about auto-fill and auto-fit for further insight into these keywords.

Using the value dense will make the grid auto-fill spaces in the grid container. This will also help create space for other, smaller grid items that may come up later.

When used in this fashion, grid-auto-flow: dense will place items without creating remainder areas and wiggle in additional ones that fit. In other words, it packs items in without leaving unused space behind. As shown below:

grid-auto-flow: column dense;
grid-auto-flow: dense;

This is a good option if you don’t care about the order and just want all spaces filled.

Alignment in CSS grid

CSS flexbox is well-known for making it easy to align items, but you can do it with CSS grid too. The same properties available in flexbox are also available with grid, such as:

  • align-items
  • align-self
  • justify-self
  • justify-items
  • justify-content

Think of justify as being along the row axis (left to right) and align along the column axis (top to bottom).

There’s also a powerful property called place-items that aligns items horizontally or vertically in one line:

place-items: x | y;
place-items: center center

Typically these alignment properties are defined on the parent container, but they can also be overridden on the children. Here’s another example:

See the Pen
CSS Grid – place items
by Emadamerho Nefe (@nefejames)
on CodePen.

Order in the court!

CSS grid has an order property that works exactly like flexbox’s to control the arrangement of items. It is written in the same fashion:

<body class="container">
    <div class="item content"><p>Content</p></div>
    <div class="item sidebar"><p>Sidebar</p></div>
</body>

Then, in your CSS:

.container {
    display: grid;
}

.sidebar{
  order: 1;
}

.content{
  order: 2;
}

Seeing as the source order of grid items has no relevance, it makes rearranging super easy, especially with media queries. See more below:

See the Pen
CSS Grid – order
by Emadamerho Nefe (@nefejames)
on CodePen.

Using CSS grid with CSS flexbox

Earlier in this article, we saw how CSS grid differs from CSS flexbox and how grid creates two-dimensional layouts, while flexbox is for one-dimensional layouts and alignments. Even though their use cases and applications differ, we can combine grid and flexbox to get the best of both worlds.

Let’s see a basic blog page scenario, for example. Here’s a screenshot of the demo we will build:

CSS Grid Blog Example

 

The image above shows a basic layout with a header, sidebar, footer, and some images.

The layouts for the header, sidebar, footer, and content sections are achieved with CSS grid, while the blog cards and their arrangement in the content section is created with CSS flexbox.

Let’s see how this works in practice, starting with the HTML markup:

<body class="container">
    //Header
    <div class="item header">
      <p>Header</p>
    </div>


    //Content
    <div class="item content">
      <p>Content</p>


      //Blog card 1
      <div class="blog-card">
        <span>Blog Title</span>
        <img src="/payment.webp" alt="a flexbox card" />
        <p>By: <span>John Doe</span></p>
      </div>


      //Blog card 2
      <div class="blog-card">
        <span>Blog Title</span>
        <img src="/payment.webp" alt="a flexbox card" />
        <p>By: <span>John Doe</span></p>
      </div>


      //Blog card 3
      <div class="blog-card">
        <span>Blog Title</span>
        <img src="/payment.webp" alt="a flexbox card" />
        <p>By: <span>John Doe</span></p>
      </div>
    </div>


    //Sidebar
    <div class="item sidebar1">
      <p>Sidebar 1</p>
    </div>


    //Footer
    <div class="item footer">
      <p>Footer</p>
    </div>
  </body>

Next, the grid layout:

.container {
  display: grid;
  gap: 10px;
  grid-template:
    "header header header" auto
    "sidebar1 content content" 1fr
    "footer footer footer" auto;
}

.header {
  grid-area: header;
}

.sidebar1 {
  grid-area: sidebar1;
}

.content {
  grid-area: content;
}

.footer {
  grid-area: footer;
}

Then, we create the blog card styles with flexbox:

 .blog-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

Setting the flex-direction to column ensures that the images and text in the card are stacked on top of each other, and the flex-start alignment ensures that all content is aligned to the left.

Lastly, we need to set up the flexbox styles that will define how the blog cards are arranged in the content section. This is important because the default behavior for divs is that they stack on top of each other because they are block elements. For this demo, we want the cards to sit beside themselves on large-screen devices and be stacked on mobile:

CSS Grid Stacked Card Blog

Because the blog cards are all in the content section, we will target the .content div and apply the styles there:

//mobile styles
.content {
  grid-area: content;
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

//large screen styles
@media screen and (min-width: 600px) {
  .content {
    flex-direction: row;
  }
}

Setting the flex-direction to column on mobile ensures the cards stack upon themselves, and for large-screen devices, we set it to row.

This demo gives us an idea of how CSS grid and flexbox complement each other well. We also see that grid children can be flexbox parents or grid parents. Here’s our final product:

See the Pen
CSS Grid – flexbox + grid
by Emadamerho Nefe (@nefejames)
on CodePen.

Conclusion

You’ve made it! We are at the end of our journey of learning the bits that helped create this amazing feature known as CSS grid. I hope what I’ve shared inspires you to start using CSS grid — it isn’t as scary as it may seem from afar!

Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps — .

Dennis Gaebel Design technologist and author that loves open source, CSS Architecture, SVG, typography, motion, interaction, and pattern-based design.

One Reply to “CSS Grid: A guide to getting started”

  1. Awesome article. Thanks for this. This would be a great addition to use hand-in-hand with flexbox – each serving their own purpose. I suppose you could easily use css to change a grid into a flexbox based on media queries.

Leave a Reply