Creating a grid in JavaScript typically means manipulating the DOM and CSS Grid properties to build dynamic layouts that respond to data and viewport changes. This guide shows you how to build a grid from scratch, calculate columns and rows, handle resizing, and update the layout efficiently. You will learn how to translate a design grid into JavaScript logic, manage item placement, and optimize for performance across browsers.
CSS Grid and JavaScript fundamentals
Modern browsers expose CSS Grid properties through the DOM, letting JavaScript read and set grid state. The primary container is an element with display: grid or display: inline-grid. Key JavaScript properties include gridTemplateColumns, gridTemplateRows, gridAutoFlow, and gridAutoColumns. You can also read computed styles with getComputedStyle and apply changes via element.style or CSS classes. This interplay enables dynamic reflow, masonry-like patterns, and data-driven layouts.
The DOM API for grids
To create a grid programmatically, start with a container element and set its style.display to grid. Then assign grid track definitions to style.gridTemplateColumns and style.gridTemplateRows using length, fraction, or auto values. You can place items with grid-column and grid-row properties or the shorthand grid. For automated layouts, gridAutoFlow controls whether new items fill rows or columns. Use appendChild or insertBefore to add items, and removeChild to delete them.
Reading and writing grid state
Reading current grid configuration starts with window.getComputedStyle(container), which returns values for grid properties as computed strings. Writing can be done directly, for example container.style.gridTemplateColumns = 'repeat(3, 1fr)', or by toggling class names to separate layout logic from styling. Keep changes minimal and batch updates with requestAnimationFrame to avoid layout thrashing when modifying many items or tracks.
Building a responsive grid from scratch
A responsive grid adjusts column count and spacing as the viewport changes. Combine CSS minmax and auto-fit in gridTemplateColumns with JavaScript that reads container width and updates track definitions when size crosses breakpoints. You can listen to resize on the window and to ResizeObserver on the grid container for precise, performant size detection. This approach keeps layout logic in JavaScript while letting CSS handle rendering and spacing.
Example 1: Simple two-column grid
To create a basic two-column grid, select the container and assign explicit column widths:
const container = document.querySelector('.grid');
container.style.display = 'grid';
container.style.gridTemplateColumns = '1fr 1fr';
container.style.gap = '1rem';Items added to the container will flow into two equal columns, wrapping as needed. For more control, use fr units, fixed pixels, or min-content depending on your design.
Example 2: Dynamic responsive grid
Calculate columns based on available width and a minimum column size:
function updateGrid(container, minColumn = 200) {
const available = container.clientWidth;
const cols = Math.max(1, Math.floor(available / minColumn));
container.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
}Hook updateGrid to window.onresize and call it once on mount after the container is rendered. This yields a fluid grid that maintains a sensible minimum width per item across devices.
Data-driven and automated grids
When the number of items is dynamic, generate markup and styles from data rather than hardcoding HTML. Loop over an array, create elements, set content, and append them to the grid container. For automated placement, set gridAutoFlow to row or column and optionally define gridAutoColumns or gridAutoRows so new items receive predictable track sizes.
Controlling placement with grid-auto-flow
grid-auto-flow determines how the browser inserts items into empty cells. A value of row fills rows left to right, top to bottom; column fills columns top to bottom. For dashboards or masonry-like layouts, combine grid-auto-flow: dense with carefully ordered source DOM to allow reordering for tighter packing.
Item placement with grid-column and grid-row
Explicit placement uses grid lines numbered from 1, where line 1 is the start edge. You can set gridItem.style.gridColumnStart and gridItem.style.gridColumnEnd, or the shorthand gridColumn. For example, item.style.gridColumn = 'span 2' makes the item span two columns. Programmatic placement is useful for ranked lists, Kanban boards, or sprite-like arrangements.
Performance and best practices
Frequent DOM and style changes can cause layout thrashing. Batch reads and writes, use documentFragment when inserting many items, and prefer toggling classes over direct style manipulation for complex states. Use ResizeObserver instead of window resize listeners for container-specific size changes, and throttle expensive recalculations with requestAnimationFrame or debouncing for rapid events.
Avoiding layout thrashing
Layout thrashing happens when you interleave read and write operations, forcing the browser to recalculate style and geometry repeatedly. To avoid this, first read all needed values, compute new state in memory, then apply changes in a single write step. This pattern is especially important inside loops that update many grid items or on window resize handlers that recalculate tracks.
Accessibility and semantic markup
Grids used for layout should preserve source order for screen readers and keyboard navigation. Use ARIA roles like grid, row, and gridcell only when the grid behaves like an interactive data grid; for visual layouts, rely on semantic HTML and natural document flow. Ensure focus order remains logical and provide visible focus indicators when items are focusable.
Comparing approaches
Different techniques suit different scenarios: simple templates for small lists, data-driven creation for dynamic content, and CSS classes for theming. The following table summarizes key attributes you can programmatically control when building grids in JavaScript.
| Attribute | Verified Detail | Source Type |
|---|---|---|
| grid-template-columns | Sets track sizes and number of columns | CSS style property |
| grid-auto-flow | Controls insertion order: row, column, dense | CSS property |
| grid-column / grid-row | Explicit item placement by line number or span | CSS shorthand / DOM property |
| ResizeObserver | Observe container size changes efficiently | Web API |
| documentFragment | Batch DOM insertions to reduce reflows | DOM API |
Common patterns and edge cases
Handle empty states by showing a friendly message when the data array is empty. Maintain consistent gutters with the gap property rather than margins on grid items to avoid alignment issues. When items have variable heights, use grid-auto-rows with minmax to keep row heights predictable. For very large datasets, consider windowing or paginating to avoid rendering thousands of DOM nodes.
Browser support and considerations
CSS Grid is widely supported in modern browsers. JavaScript APIs like ResizeObserver are available in current evergreen browsers; include polyfills if you need to support older environments. When setting styles directly, be aware that some values must match the CSS syntax exactly (e.g., repeat(auto-fit, minmax(200px, 1fr))). Test dynamic changes across breakpoints to ensure the grid behaves as intended.
Summary and key takeaways
- Set
display: gridvia JavaScript and control tracks withgridTemplateColumnsandgridTemplateRows. - Use
ResizeObserverand calculatedrepeat()patterns for responsive column counts. - Control placement with
grid-column,grid-row, andgrid-auto-flow, includingdensefor tighter packing. - Batch DOM writes, avoid layout thrashing, and prefer classes for complex theming.
FAQ
Reader questions
Can I use inline-grid with JavaScript?
Yes. Set container.style.display = 'inline-grid' to create an inline grid container. Item placement and track definitions work the same as for block grids; only the outer container behavior differs.
How do I make grid items the same height?
By default, grid items in a row align to the tallest item in that row. To enforce uniform height across all rows, avoid fixed heights on items and instead control row height with grid-auto-rows (for example, gridAutoRows: '1fr' ) or ensure consistent content constraints.
Should I use CSS classes or direct style changes?
Prefer CSS classes for theming and complex states to keep style rules maintainable. Use direct style changes for small, dynamic adjustments driven by precise measurements or user interaction, and batch them to avoid performance issues.
What is the best way to handle resizing?
Use ResizeObserver to react to container size changes and recalculate column counts with requestAnimationFrame . For simple cases, media queries in CSS may suffice; for data-driven layouts, compute tracks in JavaScript when size crosses defined breakpoints.
How do I insert items at a specific grid position?
Use explicit placement by setting grid-column (or style.gridColumnStart/End ) and grid-row (or style.gridRowStart/End ) on the item before or after insertion. The DOM insertion order determines source order for screen readers; use DOM methods like insertBefore to place nodes correctly.
Is it better to use a library like Grid (styled-components) or CSS-in-JS?
For layout-only grids, vanilla JavaScript with native CSS Grid is often simpler and performs well. Consider CSS-in-JS or grid-focused libraries when you need component-level theming, dynamic variants, or integration with a component framework that favors style props.
How do I preserve accessibility in a dynamically generated grid?
Maintain logical source order, use semantic HTML where appropriate, and add ARIA roles only when the grid behaves like an interactive data grid. Ensure keyboard navigation works and focus indicators are visible when items are interactive.