Placing, Spanning, and Density in CSS Grid


While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.


The most common thing you learn in CSS Grid is usually related to the grid container and rather than the grid items. A generic grid definition applied to the grid container is enough for a basic layout structure. However, when you need more control over the contents of the grid, the grid container is limited its uses.

For example, you may want a 4 x 2 grid but the first grid item should take up column 1 and 2 as well as row 1 and 2. Or maybe you want an item to span till the last grid column track when you have no idea what the runtime total number of the grid column track is.

Each grid item’s size is predefined and you need to find a way to size them relatively to other items in the grid container.

In this tutorial you’ll explore placing, spanning, and density in CSS Grid.

Placing Grid Items: Start Here, End There

Everything in the browser has a default style, for example, 0 or auto. There could also be other defaults that you need to be aware of before adjusting for custom values. When you have a grid, the grid items have placement values defined by grid-[x]-start and grid-[x]-end (where x can be column or row). The value is auto.

When you see a grid layout, it can help to remember that each item in that grid has a placement value no matter how symmetric the flow looks:

    .container {
      display: grid;
      grid-gap: 10px;
      grid-template-columns: repeat(5, 1fr)

    .item {
      grid-column-start: auto; /_ default _/
      grid-column-end: auto; /_ default _/
      grid-row-start: auto; /_ default _/
      grid-row-end: auto; /_ default _/

Placing Columns

Let’s adjust these default values starting with columns:

    .item:nth-child(1) {
      grid-column-start: 1;
      grid-column-end: 5;
  • We want the first item in the grid .item:nth-child(1)
  • To start at line one, grid-column-start: 1;
  • And end at line 5, grid-column-end: 5;

See the full code here.

Placing Rows

The same rules apply to row-placing:

    .item:nth-child(22) {
      grid-row-start: 1;
      grid-row-end: 4;

However, grid-row-[x] will reset the grid item default placement to start at column line 1 and row line 1, then the placement spanning will occur.

You’ll see in this code that line 22 left its spot and moved up to line 1 before spanning.

If you want it to stay at its original column track, then you have to explicitly state that:

    .item:nth-child(20) {
      grid-column-start: 3;
      grid-row-start: 5;
      grid-row-end: 10;

You can provide only start or end—you do not have to provide the placing properties in pairs. You can just provide one and the rest will stay as auto (default). The tricky thing to watch out for is that if you change only the value of grid-[x]-start, the grid item will start at the line, not the track:

    .item:nth-child(1) {
      grid-column-start: 5;

On the other hand, if you specify end only, it will start from the specified end value and span the grid inversely. For example, if you tell item 10 to end at 3, it will start from the nearest next line 2 and draw to line 3:

    .item:nth-child(10) {
      grid-column-end: 3;

You can see all the code here.

There’s also shorthand syntax for grid-[x]-start and grid-[x]-end that allows you to do away with the -start and -end code:

    .item {
      grid-column: [start] / [end];
      grid-row: [start] / [end];

Here are some examples that can replace the code samples we have written so far:

    .item {
      grid-column: 1 / 5; /__ Line 1 to 5 /
      grid-row: 1 / 4; /*_ Line 1 to 4 __/
      grid-column: 5 / auto; / Line 5 to 6 *_/
      grid-column: auto / 3; /_ Line 2 to 3 _/

Spanning: Start/End Here, Take n

We have been using the word “span” to describe the number of tracks a placement should occupy. When we say line 1 to 5, we are saying the grid item should span from 1 to 5. I usually refer to this as implicit spanning.

There is a span keyword that serves as a value. When this is used, you can refer to it as explicit spanning. Placing and spanning are flexible enough that you can use both implicit and explicit spanning as value to a single property.

Spanning Columns

Let’s start with looking at how we can span an item across a column track:

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

This is an alternative to the first example we wrote:

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

But we can include additional detail:

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

Here we end at 5 and the span 2 tracks backward.

Another example:

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

Here we start at column line 2 and end at column line 4 (after 2 jumps).

Spanning Rows

Everything you have learned so far in this tutorial about rows applies without exception:

    .item:nth-child(1) {
      grid-column: 2 / span 2;
      /_ highlight line_/
      grid-row: span 5

We are still spanning the first time, but then we’re spanning on the row axis. The browser will draw the box down to occupy five tracks.

Excess Placement and Spanning

Recall that we have explicitly defined our grid to have 5 columns. We can span or place an item from column 6 up like the following:

    .item:nth-child(1) {
      grid-column: span 10; /_ 1 _/
      grid-column: 1 / 10; /_ 2 _/
      grid-column: 7 / 10; /_ 3 _/
      grid-column: 1 / span 10; /_ 4 _/

Implicit tracks will be created to accommodate them.

Negative Integers as Values

Negative integers are as useful as the positives—they inverse the placement or spanning. Hence, instead of starting from the first column or row, the placement or spanning will start from the last column or row.

These code snippets are direct opposites:

    .item:nth-child(1) {
      grid-column: 1 / 5
    .item:nth-child(1) {
      grid-column: -1 / -5

Since we are starting at the end to draw backward as I mentioned above, there will be room for item 2 in column 1 so it gets pushed down. We will look at using density to fill up these spaces created in such cases (if the content is not symmetric).

There is a way that you can span to the end of a column or row without needing to know how many columns are defined by the grid container. This is useful when you are using auto-fill or auto-fit in defining a dynamic grid:

    .item:nth-child(1) {
      grid-column: 1 / -1

Specifying -1 for row end or column end will make the grid item span till the end of grid starting at what ever start you provided (in this case line 1).

However, this does not work on implicit grids. Therefore since we defined only column and row in the grid container, this will not do anything:

    .item:nth-child(1) {
      grid-row: 1 / -1;

For it to work, you would have to give an explicit row definition to the grid container. For example:

    .container {
      grid-template-rows: repeat(10, 30px);


In the previous examples, you will have seen some placement or spanning that caused empty spaces scattered in the middle of a grid.

You can close these spaces using grid-auto-flow property on the grid container to close up those white spaces:

    .container {
      grid-auto-flow: dense;

One thing you should be aware of is that if you have symmetric content that needs to follow an order, making the flow dense will distort that order. This is a trade-off you have to make if you want a compact design while still placing grid items irregularly.

Notice how after placing item 1 at the end, the grid comes back to continue placing 2 at the beginning of the tracks.


In this tutorial, we explored CSS Grid: placing, spanning, and density.

Creative Commons License