Avatar of Mojtaba Seyedi
Mojtaba Seyedi on (Updated on )

The grid-template-columns CSS property is part of the CSS Grid Layout specification, defining the columns of a grid container by specifying the size of the grid tracks and its line names.

.sidebar-layout {
  display: grid;
  grid-template-columns: 300px 1fr;

It’s worth noting the difference between grid lines and grid tracks as we dig into the grid-template-columns property. Grid lines refer to the names of the lines that define the grid’s columns and rows. Grid tracks refer to the length of space between grid lines.

In this example of a CSS grid layout, the grid lines are exposed and numbered along the bottom (the vertical grid column lines) and the right (the horizontal grid row lines). The track sizes are labeled along the top and left edges.


grid-template-columns: none | <track-list> | <auto-track-list> | subgrid <line-name-list>?

Full definition

<track-list> = [ <line-names>? [ <track-size> | <track-repeat> ] ]+ <line-names>?
<auto-track-list> = [ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>? <auto-repeat>
[ <line-names>? [ <fixed-size> | <fixed-repeat> ] ]* <line-names>?
<line-name-list> = [ <line-names> | <name-repeat> ]+

<line-names> = '[' <custom-ident>* ']'
<track-size> = <track-breadth> | minmax( <inflexible-breadth> , <track-breadth> ) | fit-content( [ <length> | <percentage> ] )
<track-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <track-size> ]+ <line-names>? )
<fixed-size> = <fixed-breadth> | minmax( <fixed-breadth> , <track-breadth> ) | minmax( <inflexible-breadth> , <fixed-breadth> )
<fixed-repeat> = repeat( [ <integer [1,∞]> ] , [ <line-names>? <fixed-size> ]+ <line-names>? )
<auto-repeat> = repeat( [ auto-fill | auto-fit ] , [ <line-names>? <fixed-size> ]+ <line-names>? )

<track-breadth> = <length-percentage> | <flex> | min-content | max-content | auto
<inflexible-breadth> = <length> | <percentage> | min-content | max-content | auto
<fixed-breadth> = <length-percentage>
  • Initial value: none
  • Applies to: grid containers
  • Inherited: no
  • Computed value: as specified, but with relative lengths converted into absolute lengths
  • Animation type: simple list of length, percentage, or calc, provided the only differences are in the values of the length, percentage, or calc components in the list.


/* Keyword value */
grid-template-columns: none;

/* <track-list> values */
grid-template-columns: 200px 1fr 180px;
grid-template-columns: [linename col1] 250px [line2];
grid-template-columns: [sidebar] 1fr [content] 2fr;
grid-template-columns: fit-content(50%);
grid-template-columns: minmax(200px, 1fr) minmax(100px, 1fr);
grid-template-columns: repeat(4, 1fr);
grid-template-columns: subgrid;
grid-template-columns: masonry;

/* <auto-track-list> values */
grid-template-columns: 100px repeat(auto-fill, 100px) 200px;
grid-template-columns: 100px repeat(2, 1fr auto) 200px repeat(3, 5fr);
grid-template-columns: minmax(150px, max-content) repeat(auto-fill, 180px) 15%;
grid-template-columns: [col1] 100px [col1-end] repeat(auto-fit, [line3] 400px);

/* Global values */
grid-template-columns: inherit;
grid-template-columns: initial; /* same as `none` */
grid-template-columns: revert;
grid-template-columns: unset;


This is the default value. Using the none value means grid-template-columns generates no explicit grid tracks which, in turn, means that columns are implicitly generated (i.e. made for you without having to explicitly define them) and the track sizing is defined by the grid-auto-columns property.

Note that, grid-template-columns is not the only way to create explicit grid column tracks. It can also be done using grid-template-areas property which allows for grid area that can be defined with custom names (e.g. sidebar, main, etc.).

The difference between implicit and explicit grids is nicely summed up in this post by Manuel Matuzovic.


This value can be any valid and non-negative CSS length, such as px, em and rem , among others to specify the track size (i.e. width) of the column.


This is a non-negative value relative to the inner inline size of the grid container. If the size of the grid container depends on the size of its tracks, then the percentage must be treated the same as declaring auto.

One disadvantage of using a percentage value is that, if you add gaps between the tracks, the size of the gaps will be added to the size of the tracks which may cause an overflow. Use the fr unit or auto keyword to size the tracks and prevent that from happening.


This is a non-negative value expressed in fr units and it allows you to create flexible grid tracks by specifying the size of a track as a fraction of the available space in the grid container. That way, as the grid changes widths, so do the columns, making for a more responsive grid.

For example, consider a three-column grid container that’s 900px wide. Let’s say the width of the first (left) column is fixed (i.e. it doesn’t change) at 300px while the second column takes up one fractional unit (1fr) and the third column weighs in at two fractional units (2fr):

.grid {
  display: grid;
  width: 900px;
  grid-template-columns: 300px 1fr 2fr;

The second two columns are fractional, so they’re going to take any remaining available space. And, in this case, the available space is whatever is leftover after the first column’s fixed 300px width is taken into account. After that, the second two columns divvy up what’s left according to how many fractions they get.

In this example, the remaining and available space is 600px (i.e. 900px minus 300px). The second columns gets one fraction (1fr) of that space and the third column gets two fractions (2fr). Let’s get math-y for a moment to figure out the width of one fraction of 600px:

1fr = ([Grid Container Width] - [Fixed Column Width]) / [Sum of the flex factors]
1fr = (900px - 300px) / 3
1fr = 600px / 3
1fr = 200px

Aha! One fraction is 200px. And since the second column is 1fr, it must be 200px as well. That leaves us with 400px of available space (900px300px200px) which just so happens to be twice the size of the second column.

But wait! There’s more.

Remember, a grid takes any gaps that have been added between grid tracks into account when it’s figure out how much available space there is to distribute to the fractional columns. The benefit here, of course, is that our columns will never overflow the grid container.

Let’s add a gap to the previous example:

.grid {
  display: grid;
  width: 900px;
  grid-template-columns: 300px 1fr 2fr;
  gap: 15px;

I know we have declared gap once here, but it’s actually like we’ve declared two gaps. That’s because there’s a 15px gap between each column (one between Column 1 and Column 2, and another between Column 2 and Column 3). That means we have 30px worth of gaps in our grid that have to be factored into the available space.

1fr = ([Grid Container Width] - [Gaps] - [Fixed Column Width]) / [Sum of the flex factors]
1fr = (900px - 30px - 300px) / 3
1fr = 570px / 3
1fr = 190px

I don’t know about you, but I don’t like having to do a lot of manual math. That’s what makes CSS grid awesome — it takes of it all!

minmax(min, max)

The minmax() function accepts two arguments: a minimum length value and a maximum length value. And what that tells a grid container is that the grid-template-columns can be no narrower than the minimum value, but no wider than the maximum value. Our Guide to CSS Functions has a more thorough, detailed explanation of how the function works.

Take the following example:

grid-template-columns: 200px minmax(100px, 400px);

There’s two columns here: one that’s 200px wide, and another that’s expressed as minmax(100px, 400px). Looks weird, right? But all that’s saying is that the second column must take up at least 100px of space, but no more than 400px after the first columns 200px is taken into account.

In clearer terms: As long as there’s more than 100px of available space, then the second column can flex up to 400px but has to stop there. Otherwise, the column is 100px wide.

The min and max arguments accept all the following values, except that the min value can’t be a flex value (e.g. 1fr):

<length-percentage> | <flex> | min-content | max-content | auto

So, this is invalid since the min value is a flex value:

grid-template-columns: minmax(1fr, 500px) 3fr;

But these are totally valid:

grid-template-columns: minmax(auto, 1fr) 3fr;
grid-template-columns: minmax(200%, 1fr) 1fr;


The auto keyword behaves similarly to minmax(min-content, max-content) in most cases.

Since auto track sizes can be stretched by the align-content and justify-content properties they will take up any remaining space in the grid container by default (you know, auto-matically). That said, auto track sizes can’t win a fight against fr units when allocating the remaining space — they adjust to the maximum size of their content.

Take a look at the result of these two track examples:

grid-template-columns: auto auto auto;
grid-template-columns: auto 1fr auto;

When all three columns are set to auto (which we also could have written as repeat(3, auto) — but more on that later), all they do is share equal space. But when we drop a fractional unit in the middle as the second column, the auto columns are only as wide as the content inside them and the 1fr column takes up the remaining space.

Two rows each containing three white boxes against an orange background. The top row boxes are evenly spaced with the auto keyword in them. The second row shows the two outer boxes squished toward the edges against the middle box that is labelled with 1 fractional unit, showing how those units affect the grid-template-columns property..
Demonstrating the behavior of fr unit next to auto tracks


The min-content keyword represents the smallest amount of space possible that an element can take in a grid container without overflowing its content. The content can be texts, images or videos.

Let’s say the content is “CSS is awesome.” the min-content is the width of the world “awesome” because that is the widest part of the content (wider than “CSS is”). If the column goes any narrower, then the text starts to overflow the container.

In code, that looks like this:

.grid {
  display: grid;
  grid-template-columns: min-content 1fr;
  gap: 1rem;

…which results in something like this:

One row containing two boxes. The first box says CSS is Awesome and is as wide as the word awesome. The rest of the row is taken up by the box on the right that is sized with one fractional unit.
Demonstrating the behavior of the min-content keyword in a grid layout


The max-content keyword represents the smallest size required for an element to fit all of its content without being wrapped or overflowed. I know, a name that includes “max”makes it sound like we’re dealing with a maximum size, so this value can be a bit of a brain teaser.

If we revisit our min-content example above, a grid track with a size value of max-content will grow until its content can fit into a single line. So, in the case of “CSS is awesome” the max-content is however much space those three words take up together on one line.

.grid {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 1rem;
A row containing two white boxes against an orange background. The box on the left says CSS is Awesome all on a single line and the box is as wide as the content. The box on the right fills up the remaining space in the row and is labelled with one fractional unit.
Demonstrating the behavior of the max-content keyword in a grid layout

fit-content( <length-percentage> )

The fit-content() function (there’s a solid explanation of it in our Guide to CSS Functions) uses the space available and accepts a percentage or length value as the maximum size allowed for a grid track while ensuring the track never goes beyond the max-content and never shrinks beyond a minimum. The minimum is often, but not always, equal to the min-content.

.grid {
  display: grid;
  grid-template-columns: fit-content(700px) fit-content(700px) 1fr;
  gap: 1rem;

In the figure below, the first column doesn’t expand beyond its max-content size (i.e. the width of the words “fit-content(700px)” on a single line) — it only stretches out to fit its content. The second column, however, has way more content in it, and stops stretching once it gets to a width of 700px, which is the limit set to the function:

Three white boxes in a single row against an orange background. The first box is labeled fit-content(700px). The second box is labelled fit-content(700px) but with more content so it expands until it hits the 700px limit. The third box is labeled with one fractional unit.
So, what we’re saying here for the first two columns is that they should be as wide as their maximum content, but only up to 700px. If it can fit all the content within that limit, great, stop where the max-content is. Otherwise, show as much you can up to 700px, then start breaking into additional lines.


While grid lines can always be referred to by implicit numerical name, we can also give them custom names to make the code easier to understand in a more complicated grid. Each line name can then be referenced when positioning grid items by line names.

Grid lines can have more than one name by adding names within square brackets and separating each with a whitespace:

grid-template-columns: [first content-start] 1fr [content-end sidebar-start] 300px [lastline];
Two white boxes in a single row against an orange background. The left box is labeled with one fractional unit. The right box is labeled with 300px. Black dashed lines indicate where the line tracks are and they are labeled with custom names, content-start, content-end, lastline.
Demonstrating line names and numerical index of a grid layout. The first two lines have more than one name.

Note that a line name is a <custom-ident>, a generic data type that represents any valid CSS identifier that would not be misinterpreted as a pre-defined keyword in that property’s value definition. So we can name a line super-awesome-spider-monkey but no global CSS values that could cause conflicts, like span, auto, inherit, initial and so on.

Also such identifiers are fully case-sensitive. For example, CSS treats sidebar-end and Sidebar-Name as two completely different, unrelated line names. Also, line names can be explicitly assigned with the grid-template-columns property, or implicitly assigned by named grid areas with the grid-template-areas property.

repeat([ <positive-integer> | auto-fill | auto-fit ] , <track-list>)

The repeat() function is an efficient way to define the number of grid-template-columns that are when the columns are the same. The function takes the number of columns, followed by the column size:

/* Writing this */
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;

/* is the same as this: */
grid-template-columns: repeat(5, 1fr);

Another example, this time from the specification:

grid-template-columns: 10px [col-start] 250px [col-end]
                       10px [col-start] 250px [col-end]
                       10px [col-start] 250px [col-end]
                       10px [col-start] 250px [col-end] 10px;

/* same Ss above, except easier to write */
grid-template-columns: repeat(4, 10px [col-start] 250px [col-end]) 10px;

auto-fill and auto-fit

Apart from an integer that shows the number of repetitions, two keywords of auto-fill and auto-fit are also allowed to be the first argument of the repeat() function.

As opposed to a fixed number of repetitions, these keywords allow you to display as many columns of a certain size as would fit into a row of a grid container.

The two keywords of auto-fill and auto-fit are pretty similar. Both try to automatically fit as many grid item as possible into a grid column before wrapping to a new line. The difference? auto-fill fills as many columns as it can and, when it runs out of items, it leaves whatever space is leftover empty. auto-fit, on the other hand, will increase the width of the columns so there is no empty space leftover.

Two rows office white boxes against an orange background. Both have an example of grid-template-columns, but one uses the auto-fill keyword and the other uses auto-fit.
Demonstrating how auto-fill creates empty columns but auto-fit stretches the existing ones.

There are a few limitations when using the repeat() function that are worth calling out:

  • It is unable to nest a repeat() function within a repeat() function.
/* This is invalid */
grid-template-columns: repeat(4, 1fr repeat(5, 1fr));
  • It does not accept intrinsic or flexible length values (e.g. fr units) when using auto-fill or auto-fit. It only accepts an explicit track size so it is able to calculate the number of columns that go into the grid container.
/* This is invalid */
grid-template-columns: repeat(auto-fit, 1fr);
  • It is ok, however, to use and intrinsic or flexible sizing as the function’s second value as long as the first value is a positive number.
/* These are invalid */
grid-template-columns: repeat(auto-fit, 1fr);
grid-template-columns: repeat(min-content, 1fr);

/* This is valid */
grid-template-columns: repeat(6, 1fr);
  • It can only be used once in the grid-template-columns track list when using auto-fill or auto-fit. We can use multiple repeat() functions, however, as long as the second value is fixed.
/* These are invalid */
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)) 100px repeat(auto-fill, minmax(5rem, auto));
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)) 100px repeat(3, 1fr);

/* This is valid */
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)) 100px repeat(3, 200px);


Subgrid is considered experimental and is only supported in Firefox at the time of this writing.

Can we have a grid within a grid? Of course we can! That’s thanks to the subgrid keyword value. Declaring this allows the inner (child) grid to share the track lines of the outer (parent) grid, allowing for more consistent track lines without having to re-declare them all over again.

So, say we have a grid within a grid:

.grid-container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
.grid-container .grid-item {
  display: grid;  

We can turn that nested grid into a subgrid to share the track lines between with the parent grid:

.grid-container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
.grid-container .grid-item {
  display: grid;  
  grid-template-columns: subgrid;

Now, the child grid’s items will placed and sized according to the main grid container’s track sizing. For example, consider the following HTML:

<div class="grid">
  <div class="item"></div>

  <div class="item subgrid">
    <div class="subitem"></div>
    <div class="subitem"></div>

  <div class="item"></div>
  <div class="item"></div>

Now let’s make a subgrid:

 .grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr); /* Establish a grid with 4 tracks */
.item {  
  grid-column: 1/-1; /* Make all items as wide as the 4 tracks */
.subgrid {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 3/-1; /* Make the subgrid item size, half of the other items */

Here you can see that the subgrid items adjust to the track sizing of the parent grid:

A four-by-four grid of white boxes. The second row contains two subgrid items that are flush to the third and four tracks of the parent grid. All of the track lines are labeled and show how the subgrid items use the parent tracks but still start with a track line of one.
A subgrid layout

The gaps in a subgrid, are inherited by the parent, but that can be overridden with a different gap value on the subgrid element:

.grid-container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem; /* This is inherited by the child grid */
.grid-container .grid-item {
  display: grid;  
  grid-template-columns: subgrid;
  gap: 0.5rem; /* This overrides the parent grid's gap */

A subgrid has its own line numbers. For instance, when you use line number 1 in your subgrid, it points to the first line of the child grid, not the parent. So, even if a subgrid starts on the third track line of the parent grid, the first track line of the subgrid will still be 1 and not 3.

And, of course, we can declare custom grid line names in a subgrid:

.grid-container .grid-item {
  display: grid;  
  grid-template-columns: subgrid [sidebar-start] [sidebar-end] [main-start] [main-last];

Oh, and we can totally use the repeat() function here as well to specify track line names:

.grid-container .grid-item {
  display: grid;  
  grid-template-columns: subgrid repeat(2, [a] 1fr [b]);

One thing to note with that, though, is that the repeat() function merges any track line-names that are next to each other.

/* These are equivalent */
grid-template-columns: repeat(2, [a] 1fr [b]);
grid-template-columns: [a] 1fr [b a] 1fr [b];

Note that in a subgrid you cannot have any implicit tracks in that dimension. So if you add more grid items to the mix, they will be placed in the existing explicit tracks of the subgrid. You can see the result of adding one more child to the subgrid of the previous example in the following image:

The same subgrid illustration but showing three grid items on two rows that are squished together with no vertical spacing.
Subgrid doesn’t generate implicit tracks.


You know what we mean by masonry, right? It’s that cool layout where items in a grid are sort of tiled and pieced together based on the vertical space that’s available in a column. It was popularized years ago by David DeSandro’s JavaScript library of the same name.

An illustration of a five-column masonry layout of photos with thick rounded white borders against an orange background.
A masonry layout

Thanks to CSS Grid, masonry is going to be something we can accomplish without resorting to JavaScript. At the time of this writing, however, it’s still quite a way out as part of the Level 3 of the CSS Grid specification, which is currently in Editor’s Draft. It is, however, available in Firefox behind a flag.

Screenshot of the Firefox about:config settings with a search for masonry and the option to enable the masonry flag.

We’re likely to see changes in the specification between now and the time it is actually released. But here’s how it currently looks:

.container {
  display: grid;
  grid-template-columns: masonry;
  grid-template-rows: repeat(4, 1fr);


Yay, demo time! I have a few examples that show off the powers of the grid-template-columns property that you may very well bump into when working with a CSS Grid layout.

A simple sidebar layout can be achieved with the following CSS:

.layout {
  display: grid;
  grid-template-columns: 1fr 250px;

And here is the result:

Responsive and flexible card layout with dynamic number of items

This might be the most magical snippet of the bunch. Say you want to pack in as many columns into a grid row before it wraps to a new line. With this snippet, we combine the powers of repeat(), auto-fit, and minmax() to to do just that:

grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

This way, we get a repeating number of columns (thanks to repeat()) that will fill all of the available space of the grid (thanks to auto-fit) while never shrinking narrower than 250px and each taking up an equal fraction of space (thanks to minmax()).

Aligning forms elements

Have you ever found yourself wrestling with CSS to get form controls to line up? The classic case is a form with stacked text inputs where the input label is on the left and the input is on the right. When the labels are uneven, so is the starting point of the inputs, making things look more like a staggered staircase than a neatly stacked form.

With CSS Grid, however, we can tell the left column that holds the input label to display at max-content and the right column that holds the inputs to take up the rest of the space.

grid-template-columns: max-content 1fr;

No extra elements or wrappers needed!

Equal columns

If you use the fr unit to size a grid’s columns, the available space is distributed after the content decides how much space each column needs. If you want three equally-sized columns, you might think to do this:

grid-template-columns: 1fr 1fr 1fr;

And that’s not totally wrong! But since the minimum size of a grid layout is auto, it depends on the length of the content to get truly equal columns. So, we take out the minimum size but supplying a zero value in minmax() functions for each column like this:

grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);

Browser support

More information