Порівняння flexbox та CSS grid
Flexbox будує макети вздовж однієї осі (рядок або стовпець); CSS Grid будує на двох осях одразу (рядки і стовпці разом).
Теорія
Коротко
- Flexbox = один ряд сидінь в автобусі: елементи течуть вздовж однієї осі та розтягуються або стискаються
- Grid = схема кварталу: ти визначаєш вулиці І проспекти, елементи розміщуються на перетинах
- Головна різниця: Flexbox розподіляє простір вздовж однієї осі; Grid контролює обидві одночасно
- Одновимірний макет (навбар, група кнопок, ряд карток)? Flexbox. Сторінка або дашборд? Grid
- Вони добре поєднуються: Grid для каркасу сторінки, Flexbox всередині кожної клітинки
Швидкий приклад
Однаковий візуальний результат, різний рівень контролю:
<!-- Flexbox: елементи течуть вздовж головної осі -->
<div class="flex-row">
<div class="item">Nav</div>
<div class="item">Logo</div>
<div class="item">Login</div>
</div>
<!-- Grid: треки визначено заздалегідь -->
<div class="grid-layout">
<header>Header</header>
<aside>Sidebar</aside>
<main>Content</main>
</div>/* Flexbox: одна вісь, простір розподіляється між елементами */
.flex-row {
display: flex;
justify-content: space-between;
align-items: center;
}
/* Grid: дві осі, зони визначено явно */
.grid-layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 60px auto;
grid-template-areas:
"header header"
"sidebar content";
}Flexbox нічого не знає про рядки. Grid не займається потоком вмісту. Саме в цьому розриві й лежить рішення про вибір.
Ключова різниця
Flexbox починає з контенту і розподіляє навколо нього простір. Елементи розтягуються, стискаються, переносяться на новий рядок, але завжди вздовж однієї осі. Grid починає зі структури: спочатку визначаєш треки, потім елементи їх заповнюють. Тому Grid дозволяє елементам перекривати кілька рядків і стовпців незалежно, а елементи Flexbox завжди рухаються по потоку. Вкласти Flexbox всередину клітинки Grid - звична практика, яка покриває більшість реальних макетів.
Коли що використовувати
- Навбар з логотипом і посиланнями: Flexbox (
justify-content: space-between) - Ряд карток однакової висоти: Flexbox (
align-items: stretch) - Макет з хедером, сайдбаром, контентом і футером: Grid
- Дашборд з асиметричними панелями різного розміру: Grid (
grid-template-areas) - Центрування одного елемента: підходять обидва (
display: flex; place-items: centerабоdisplay: grid; place-items: center) - Галерея де деякі елементи займають кілька колонок: Grid (
grid-column: span 2) - Компонент всередині клітинки Grid, наприклад група кнопок у nav-зоні: Flexbox
Таблиця порівняння
| Аспект | Flexbox | CSS Grid |
|---|---|---|
| Виміри | 1D (рядок або стовпець) | 2D (рядки і стовпці) |
| Головна сила | Розподіл простору вздовж однієї осі | Явне розміщення та перекриття елементів |
| Поведінка контейнера | Елементи займають доступний простір | Треки визначено заздалегідь |
| Керування елементами | flex-grow, flex-shrink, flex-basis, order | grid-area, grid-column, grid-row, place-self |
| Вирівнювання | justify-content, align-items, align-self | justify-items, align-items, place-items, place-content |
| Перекриття елементів | Складно | Нативна підтримка через спільну grid-зону |
| Підтримка браузерів | IE10+, повна підтримка в сучасних | IE11 частково; subgrid з Chrome 117 |
| Коли використовувати | Навбари, кнопки, ряди карток, центрування | Макети сторінок, дашборди, галереї |
Як браузер це обробляє
Коли браузер бачить display: flex, він створює flex formatting context. Спочатку обраховує гіпотетичний розмір кожного елемента через flex-basis, потім розподіляє залишковий простір відповідно до flex-grow. Перенос на новий рядок відбувається тільки при flex-wrap: wrap.
display: grid створює grid formatting context. Браузер будує карту треків з grid-template-columns і grid-template-rows, потім розміщує елементи або за явним grid-area, або через алгоритм автоматичного розміщення. Grid розраховує треки одноразово на початку; Flexbox перераховує при кожному reflow. Для довгих списків (1000+ елементів) Flexbox зазвичай швидший, бо кешування рядків простіше ніж розрахунок grid-треків.
Типові помилки
Помилка 1: flex-direction: column для макету з сайдбаром і основним контентом
/* Неправильно: flex-direction column складає елементи зверху вниз */
.layout {
display: flex;
flex-direction: column;
}
/* Виправлення: Grid створює справжню структуру з колонками поруч */
.layout {
display: grid;
grid-template-areas: "sidebar main";
grid-template-columns: 240px 1fr;
}Це найпоширеніша помилка з макетами, яку я бачу на код-рев'ю. Розробники, які добре знають Flexbox, тягнуться до flex-direction: column коли потрібні колонки поруч, а він просто складає елементи вертикально.
Помилка 2: align-items: center на Grid для повного центрування
/* Неправильно: центрує тільки по поперечній осі кожного треку */
.grid { display: grid; align-items: center; }
/* Виправлення: центрує по обох осях */
.grid { display: grid; place-items: center; }align-items відповідає за block-вісь. justify-items - за inline-вісь. place-items - скорочення для обох, і саме воно потрібне для справжнього центрування.
Помилка 3: justify-content: space-between з елементами різного розміру
/* Виглядає нерівномірно коли ширина елементів різна */
.nav {
display: flex;
justify-content: space-between;
}
/* Виправлення: gap дає передбачуваний відступ */
.nav {
display: flex;
gap: 1rem;
}space-between розподіляє вільний простір між елементами. Якщо ширина елементів різна, проміжки візуально відрізняються. gap дає однакові відступи незалежно від розміру елементів і не виходить за зовнішні краї після перенесення рядка.
Помилка 4: Відсутність min-height: 0 у дочірніх елементах flex-колонки
/* Елемент відмовляється стискатись і виходить за межі батька */
.sidebar-item {
display: flex;
flex-direction: column;
/* Відсутній: min-height: 0 */
}
/* Виправлення */
.sidebar-item {
display: flex;
flex-direction: column;
min-height: 0; /* Дозволяє стискатись нижче розміру контенту */
overflow: auto;
}Flex-елементи за замовчуванням мають min-height: auto, що не дозволяє стискатись нижче розміру контенту. В колонкових макетах це викликає переповнення. min-height: 0 знімає це обмеження.
Помилка 5: Subgrid без відповідних треків у батьківському елементі (Chrome 117+)
/* Батько визначає тільки два стовпці */
.grid-parent {
display: grid;
grid-template-columns: 1fr 2fr;
}
/* Subgrid автоматично використовує auto якщо кількість треків не збігається */
.grid-child {
display: grid;
grid-template-columns: subgrid;
}Subgrid успадковує треки батьківського елемента. Якщо батько не визначає потрібні треки, subgrid повертається до auto і вирівнювання ламається без жодних помилок. Спочатку визнач явні треки в батьківському елементі.
Де це зустрічається
- React і Next.js: Flexbox для компонентів (кнопки та навбари в Chakra UI); Grid для структури додатку (дашборд Vercel)
- Tailwind CSS:
flex space-x-4для inline-списків;grid-cols-12для каркасу сторінок у темах Shopify - Bootstrap 5:
.d-flex .justify-content-betweenпобудований на Flexbox; складні форми з кількома колонками використовують Grid - Material UI: Flexbox у компонентах Card і List; Grid у таблицях даних
Питання на співбесіді
Q: Яка різниця між justify-content і justify-items?
A: justify-content розподіляє простір між треками або елементами на рівні контейнера. justify-items вирівнює кожен елемент всередині його власної grid-зони. У Flexbox є justify-content, але немає justify-items - ця властивість працює тільки в Grid.
Q: Чим відрізняється flex-basis від width?
A: flex-basis - це початковий розмір елемента до застосування flex-grow і flex-shrink. width - це розмір у блоковій моделі. Якщо задані обидва, у flex-контексті перемагає flex-basis. flex-basis: 0 змушує всі елементи починати з нуля і рівномірно зростати.
Q: Як працює grid-auto-flow: dense?
A: Коли spanning-елементи залишають порожні місця в сітці, dense наказує браузеру заповнювати їх меншими елементами з пізніших позицій у DOM. Це покращує щільність сітки, але змінює візуальний порядок відносно DOM-порядку, що може ускладнити навігацію клавіатурою.
Q: Чому gap краще за margins у flex або grid-контейнерах?
A: gap застосовується тільки між елементами, не на зовнішніх краях. Margins на дочірніх елементах накопичуються і можуть створювати подвійні відступи по краях або після перенесення рядка. gap коректно працює в обох контекстах.
Q: Що таке subgrid і коли він реально потрібен?
A: Subgrid дозволяє вкладеній сітці успадковувати розміри треків батьківської. Без нього картка всередині Grid-клітинки визначає власні внутрішні треки, які не збігатимуться з картками у сусідніх клітинках. З subgrid всі картки ділять однакові row-треки і їх контент вирівнюється по колонках. Доступно в Chrome 117+, Firefox 71+, Safari 16+. Для старших браузерів CSS custom properties на спільному батьківському елементі - практична альтернатива.
Приклади
Навбар на Flexbox (React, production-патерн)
function Navbar() {
return (
<nav style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '1rem 2rem',
borderBottom: '1px solid #eee'
}}>
<span style={{ fontWeight: 'bold' }}>Logo</span>
<ul style={{ display: 'flex', listStyle: 'none', gap: '2rem', margin: 0 }}>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
<button>Login</button>
</nav>
);
}Логотип зліва, навігація з рівними відступами, кнопка входу справа. Внутрішній ul теж flex-контейнер. Це і є патерн «Grid для каркасу, Flexbox всередині» на практиці.
Макет сторінки на CSS Grid
.app {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: 60px 1fr 40px;
grid-template-areas:
"header header"
"sidebar content"
"footer footer";
min-height: 100vh;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
main { grid-area: content; }
footer { grid-area: footer; }Іменовані зони роблять структуру читабельною з першого погляду. Зміни grid-template-columns на 1fr в media query - і весь макет перебудовується автоматично.
Галерея з перекриттям колонок
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
/* Виділений елемент займає дві перші колонки */
.gallery .featured {
grid-column: span 2;
}Перкриття елементів - це те, де Grid не має аналога у Flexbox. Зробити один flex-елемент шириною двох колонок при збереженні спільної сітки неможливо. Grid підтримує це нативно.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.