158

What I wanna do is to make a CSS grid with a dynamic number of cells. For the sake of simplicity, let's assume there will always be four cells per row. Can I specify a grid with such a dynamic number of rows?

To make it easier, here's the Flexbox implementation:

const COLORS = [
  '#FE9',
  '#9AF',
  '#F9A',
  "#AFA",
  "#FA7"
];

function addItem(container, template) {
  let color = COLORS[_.random(COLORS.length - 1)];
  let num = _.random(10000);
  
  container.append(Mustache.render(template, { color, num }));
}

$(() => {
  const tmpl = $('#item_template').html()
  const container = $('#app');
  
  for(let i=0; i<5; i++) { addItem(container, tmpl); }
  
  $('#add_el').click(() => {
    addItem(container, tmpl);
  })
  
  container.on('click', '.del_el', (e) => {
    $(e.target).closest('.item').remove();
  });
});
.container {
  width: 100%;
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
}
.container .item {
  flex: 0 0 calc(25% - 1em);
  min-height: 120px;
  margin: 0.25em 0.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app" class="container">
</div>

<button id="add_el">Add element</button>

<template id="item_template">
  <div class="item" style="background: {{color}}">
    <p>{{ num }}</p>
    <p>
      <button class="del_el">Delete</button>
    </p>
  </div>
</template>

P.S. Apparently, I wasn't clear enough the first time... I want to recreate this effect using the latest CSS Grid Layout.

2
  • 3
    do you wanna always have 4 cells per row or do you wanna to have those cells dynamic per row ? Unclear
    – dippas
    Commented Mar 30, 2017 at 11:02
  • 3
    Dynamic cells per row would be cool, but I'm content with having 4 cells per row right now. Commented Mar 30, 2017 at 11:19

7 Answers 7

107

Okay, after reading the MDN reference, I found the answer! The key to dynamic rows (or columns) is the repeat property.

const COLORS = [
  '#FE9',
  '#9AF',
  '#F9A',
  "#AFA",
  "#FA7"
];

function addItem(container, template) {
  let color = COLORS[_.random(COLORS.length - 1)];
  let num = _.random(10000);
  
  container.append(Mustache.render(template, { color, num }));
}

$(() => {
  const tmpl = $('#item_template').html()
  const container = $('#app');
  
  for(let i=0; i<5; i++) { addItem(container, tmpl); }
  
  $('#add_el').click(() => {
    addItem(container, tmpl);
  })
  
  container.on('click', '.del_el', (e) => {
    $(e.target).closest('.item').remove();
  });
});
.container {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(auto-fill, 120px);
  grid-row-gap: .5em;
  grid-column-gap: 1em;
}

.container .item {
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app" class="container">
</div>

<button id="add_el">Add element</button>

<template id="item_template">
  <div class="item" style="background: {{color}}">
    <p>{{ num }}</p>
    <p>
      <button class="del_el">Delete</button>
    </p>
  </div>
</template>

P.S. Or you can use grid-auto-rows in my particular example.

7
  • 15
    grid-template-columns: repeat(auto-fill, Xpx); ... finally I found it :) thank you!
    – Sandro
    Commented Sep 6, 2017 at 14:11
  • @Sandro I think I'd actually recommend using grid-auto-rows but AFAIK it wouldn't work if the "auto rows" aren't inserted last. Anyway, good luck! Commented Sep 6, 2017 at 14:29
  • 2
    There are grid-auto-columns too! Commented Sep 6, 2017 at 16:35
  • Yeah but I didn't get them to work to use a dynamic amount of colums. It just used one.
    – Sandro
    Commented Sep 6, 2017 at 17:25
  • 7
    Would setting grid-auto-flow to column help? codepen.io/art-solopov/pen/xLNYVw Commented Sep 6, 2017 at 21:33
89

Simply use below code to generate grid columns automatically

.container {
  display: grid;
  grid-auto-flow: column;
}
4
  • 3
    but this change gives almost equal width. How can we make it to render with respect to child-column width. Say my first column is 220px, and second column i want is auto or say 200px auto auto or 220px auto 220px? Commented Dec 28, 2020 at 8:26
  • it is good idea to keep inline blocks without knowing the length and count
    – 3gwebtrain
    Commented Apr 7, 2021 at 16:38
  • This worked for me better than other answers cause I had a dynamic number of icons I wanted to show on a 'tray' container.
    – 10110
    Commented Jun 5, 2022 at 0:14
  • Grid auto flow will kind of mess with the ordering in your layout. If you are trying to mimic something like a table using grid, this is not a good approach.
    – sin2akshay
    Commented Nov 28, 2022 at 12:26
23

For those landing here looking for a way to have a sort of dynamic table, with items wrapping to new rows, and still being aligned across rows, a pretty good solution is to use flex with flex-wrap and flex: 1 for all elements:

.container {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

.container .item {
  flex: 1;
}
3
  • 10
    Can also be done with css grid following this nice article: css-tricks.com/…
    – Uri Klar
    Commented May 4, 2020 at 12:47
  • @UriKlar You should really post that as a reply, that is a much cleaner solution Commented Jan 26, 2022 at 5:54
  • Good solution for a collection of card like my E-commerce project. Commented Feb 17, 2022 at 19:22
15

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

What I came to look for was how to make the browser calculate the appropriate number of columns while keeping the rows stretched. I found my answer in the mdn docs.

The important thing is to set a column min width. In my example I used 200px.

Fun fact: Instead of using 200px, you can use min(200px, 100%) which lets the items shrink even smaller when the container has not enough space. Similar to min-width: 100%.

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

.container:has(+input:checked) {
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
}


.fancy-colors {
  border: 2px solid #f76707;
  border-radius: 5px;
  background-color: #fff4e6;
  width: 600px;
  resize: horizontal;
  overflow: auto;
  position: relative;
  &::after {
    position: absolute;
    bottom: 0;
    right: 10px;
    content: "Resize me!";
    color: grey;
    font-size: 0.8em;
  }
  > * {
    border: 2px solid #ffa94d;
    border-radius: 5px;
    background-color: #ffd8a8;
    padding: 0.8em;
    color: #d9480f;
  }
}
<div class="container fancy-colors">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
  <div>Four</div>
  <div>Five</div>
  <div>Six</div>
  <div>Seven</div>
</div>

<input type="checkbox">min(200px, 100%)</input>

5

Something like this?

$(document).ready(function() {
 //Prepare Element selectors
  var cssElem = $("#Dynam"), rowElem = $("#rows"), columnElem = $("#columns"), appElem = $("#app");
  var noItems = $(".item").length,defaultColumns = 4;
    
  //Init default state
  cssElem.html(".container .item {flex: 0 0 calc(" + (100 / defaultColumns) + "% - 1em);}");
  columnElem.val(defaultColumns);
  rowElem.val(Math.ceil(noItems / columnElem.val()));

  //Add listeners to change
  appElem.on("DOMSubtreeModified", function() {
    noItems = $(".item").length;
    rowElem.val(Math.ceil(noItems / columnElem.val()));
  });
  columnElem.on("change", function() {
    rowElem.val(Math.ceil(noItems / columnElem.val()));
    cssElem.html(".container .item {flex: 0 0 calc(" + (100 / columnElem.val()) + "% - 1em);}");
  });
  rowElem.on("change", function() {
    columnElem.val(Math.ceil(noItems / rowElem.val()));
    cssElem.html(".container .item {flex: 0 0 calc(" + (100 / columnElem.val()) + "% - 1em);}");
  });
});

const COLORS = ['#FE9', '#9AF', '#F9A', "#AFA", "#FA7"];

function addItem(container, template) {
  let color = COLORS[_.random(COLORS.length - 1)];
  let num = _.random(10000);

  container.append(Mustache.render(template, {
    color,
    num
  }));
}

$(() => {
  const tmpl = $('#item_template').html()
  const container = $('#app');

  for (let i = 0; i < 5; i++) {
    addItem(container, tmpl);
  }

  $('#add_el').click(() => {
    addItem(container, tmpl);
  })

  container.on('click', '.del_el', (e) => {
    $(e.target).closest('.item').remove();
  });
});
.container {
  width: 100%;
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
}

.container .item {
  min-height: 120px;
  margin: 0.25em 0.5em;
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<style id="Dynam"></style>
<button id="add_el">Add element</button> rows:
<input id="rows" /> columns:<input id="columns" />

<div id="app" class="container">
</div>

<template id="item_template">
  <div class="item" style="background: {{color}}">
    <p>{{ num }}</p>
    <p>
      <button class="del_el">Delete</button>
    </p>
  </div>
</template>

2
  • 2
    I'm sorry if I wasn't clear enough, I wanted to recreate the effect I had using the recently-created CSS Grid layout. Commented Mar 30, 2017 at 11:57
  • Oh? That's really fresh caniuse.com/#feat=css-grid ... I am quite surprised that it is being adopted by majority of browsers at once. Well sorry, i have zero knowledge about css grid at the moment.
    – Adam K.
    Commented Mar 30, 2017 at 12:01
5

Here it is a very simple example of dynamic columns in grid

.container {
    display: grid;
    grid-template-columns: repeat(auto-fit, 250px);
    grid-row-gap: 5px;
    grid-column-gap: 5px;
    justify-content: center;
    width: 90%;
    margin: 0 auto;
    list-style: none;
}

.container>.item {
    padding: 5px;
    background-color: gray;
}
<div class="container">
    <div class="item">item1</div>
    <div class="item">item2</div>
    <div class="item">item3</div>
    <div class="item">item4</div>
</div>

4

You don't need to use repeat. Instead you can just set a variable --grid-template-columns from your javascript code.

rootEl.style.setProperty('--grid-template-columns' theGridTemplateColumnsValue)

theGridTemplateColumnsValue is a string that can also contains other css variables. This way you can have a dynamic number of columns per row.

1
  • 5
    The specification for CSS Grid has auto-fit, auto-fill, repeat, and also grid-auto-columns and grid-auto-rows to use, in collaboration with each other and flexbox in order to solve layout challenges without the need to bring JavaScript into the picture. CSS Grid was created for all two-dimensional layout challenges. (Flexbox is meant for flow control.) No JavaScript required. See answer below by the OP, as they discovered in the specification of CSS Grid. developer.mozilla.org/en-US/docs/Web/CSS/grid
    – weo3dev
    Commented Jan 25, 2020 at 2:59

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.