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.
<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.
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.
<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