Tutorials 3 min read

How to create a grid toggle with Tailwind CSS: JavaScript and Alpine.js

Build a grid layout toggle with Tailwind CSS, two ways: vanilla JavaScript or Alpine.js. One button switches the container between two and four columns.

Today we are building a grid toggle: one button that switches a grid between two layouts. We’ll do it twice, first with vanilla JavaScript, then with Alpine.js, so you can pick whichever fits your stack.

What is a grid toggle?

A grid toggle (or layout switcher) lets the user flip a container between two grid layouts, typically a compact four-column view and a roomier two-column view. All the visual work happens in Tailwind’s grid-cols-* utilities; the script only swaps which class is on the container.

Use cases

  • Product listings: let shoppers choose between a dense grid and larger cards.
  • Image galleries: switch between overview and detail-friendly layouts.
  • Blog or news indexes: compact headlines or bigger preview cards.
  • Dashboards: fit more widgets on screen when the user wants density.

The markup

  • <button id="toggleButton">: the button that toggles the layout.
  • <div id="gridContainer">: the grid we grab with JavaScript to swap its classes.

Classes are removed for brevity, but I’ll keep those classes relevant to the tutorial.

html
<div>
  <div>
    <!-- Toggle Button -->
    <button id="toggleButton">Switch to 2 Columns</button>
  </div>
  <!-- Grid Layout -->
  <div id="gridContainer" class="grid grid-cols-4 ...">
    <!-- Items in the Grid -->
    <div class="...">Item 1</div>
    <div class="...">Item 2</div>
    <div class="...">Item 3</div>
    <div class="...">Item 4</div>
    <div class="...">Item 5</div>
    <div class="...">Item 6</div>
    <div class="...">Item 7</div>
    <div class="...">Item 8</div>
  </div>
</div>

The script

  • document.addEventListener("DOMContentLoaded", () => {: waits until the page is loaded.
  • const isFourColumns = gridContainer.classList.toggle("grid-cols-4");: toggles the four-column class and remembers whether it’s now on.
  • gridContainer.classList.toggle("grid-cols-2", !isFourColumns);: applies the two-column class whenever four columns is off.
  • toggleButton.textContent = ...: updates the button label to describe the next state.
js
document.addEventListener("DOMContentLoaded", () => {
  const toggleButton = document.getElementById("toggleButton");
  const gridContainer = document.getElementById("gridContainer");

  toggleButton.addEventListener("click", () => {
    const isFourColumns = gridContainer.classList.toggle("grid-cols-4");
    gridContainer.classList.toggle("grid-cols-2", !isFourColumns);
    toggleButton.textContent = isFourColumns
      ? "Switch to 2 Columns"
      : "Switch to 4 Columns";
  });
});

The Alpine.js version

Same component, no separate script: the state lives in x-data and the classes are bound with :class.

  • x-data="{ isFourColumns: true }": stores the state of the toggle.
  • @click="isFourColumns = !isFourColumns": flips it.
  • :class="{'grid-cols-4': isFourColumns, 'grid-cols-2': !isFourColumns}": applies the matching layout.
  • x-text: keeps the button label in sync.
html
<div x-data="{ isFourColumns: true }">
  <div>
    <button
      @click="isFourColumns = !isFourColumns"
      x-text="isFourColumns ? 'Switch to 2 Columns' : 'Switch to 4 Columns'"
    ></button>
  </div>
  <!-- Grid Layout -->
  <div
    :class="{'grid-cols-4': isFourColumns, 'grid-cols-2': !isFourColumns}"
    class="mt-4 grid gap-2"
  >
    <!-- Items in the Grid -->
    <div>Item 1</div>
    <div>Item 2</div>
    <div>Item 3</div>
    <div>Item 4</div>
    <div>Item 5</div>
    <div>Item 6</div>
    <div>Item 7</div>
    <div>Item 8</div>
  </div>
</div>

You can try the Alpine version separately: live demo and source code.

Which one should you use?

If the page already loads Alpine, the x-data version keeps everything in the markup. If not, the vanilla script does the same job without adding a dependency.

Conclusion

One button, two Tailwind classes, and a couple of lines of state: that’s the whole component, in whichever flavor your project prefers.

Hope you enjoyed this tutorial and have a great day!

/Michael Andreuzza