Fragment в React: группировка элементов без лишних узлов DOM

16 марта 2026
Автор

Олег Марков

Fragment — группировка элементов без лишнего DOM

Fragment позволяет группировать несколько дочерних элементов без добавления лишних узлов в DOM. Это решает проблему обязательного единственного корневого элемента при возврате нескольких элементов из компонента.

import { Fragment } from 'react';

function Component() {
  return (
    <Fragment>
      <h1>Заголовок</h1>
      <p>Параграф</p>
    </Fragment>
  );
}

Проблема: лишняя обёртка

Без Fragment приходится оборачивать несколько элементов в <div>, что засоряет DOM ненужными узлами:

// ❌ Плохо — лишний div в DOM
function TableRow() {
  return (
    <div>          {/* Лишний div нарушает семантику таблицы! */}
      <td>Ячейка 1</td>
      <td>Ячейка 2</td>
      <td>Ячейка 3</td>
    </div>
  );
}

// ✅ Хорошо — Fragment не создаёт DOM-узел
function TableRow() {
  return (
    <Fragment>
      <td>Ячейка 1</td>
      <td>Ячейка 2</td>
      <td>Ячейка 3</td>
    </Fragment>
  );
}

Краткий синтаксис <></>

React поддерживает сокращённую запись Fragment с помощью пустых тегов <> и </>:

function Component() {
  return (
    <>
      <h1>Заголовок</h1>
      <p>Параграф первый</p>
      <p>Параграф второй</p>
    </>
  );
}

Краткая запись <></> — это синтаксический сахар для <Fragment></Fragment>. Оба варианта создают одинаковый результат.

Когда нужен Fragment, а не <></>

Краткий синтаксис <></> не поддерживает атрибуты. Если нужно передать key (например, при рендере списков), используйте полную запись <Fragment key={...}>:

// ❌ Не работает — краткий синтаксис не поддерживает атрибуты
function List({ items }) {
  return items.map(item => (
    <key={item.id}>  {/* Синтаксическая ошибка! */}
      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </>
  ));
}

// ✅ Правильно — используем Fragment с key
import { Fragment } from 'react';

function List({ items }: { items: Array<{ id: number; term: string; description: string }> }) {
  return items.map(item => (
    <Fragment key={item.id}>
      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </Fragment>
  ));
}

Практические примеры

Таблицы и списки определений

Fragment особенно полезен там, где структура DOM строго регламентирована:

// Таблица — строки должны содержать только <td>/<th> без лишних обёрток
function UserTable({ users }: { users: User[] }) {
  return (
    <table>
      <tbody>
        {users.map(user => (
          <Fragment key={user.id}>
            <tr>
              <td>{user.name}</td>
              <td>{user.email}</td>
            </tr>
            {user.isAdmin && (
              <tr className="admin-row">
                <td colSpan={2}>Администратор</td>
              </tr>
            )}
          </Fragment>
        ))}
      </tbody>
    </table>
  );
}
// Список определений
function Glossary({ terms }: { terms: Array<{ word: string; definition: string }> }) {
  return (
    <dl>
      {terms.map(term => (
        <Fragment key={term.word}>
          <dt>{term.word}</dt>
          <dd>{term.definition}</dd>
        </Fragment>
      ))}
    </dl>
  );
}

Возврат нескольких элементов из компонента

// Компонент возвращает несколько элементов без обёртки
function UserInfo({ user }: { user: User }) {
  return (
    <>
      <Avatar src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
      <ContactLinks links={user.contacts} />
    </>
  );
}

Условный рендер нескольких элементов

function Notification({ type, message, details }: NotificationProps) {
  return (
    <>
      <p className={`notification notification--${type}`}>{message}</p>
      {details && (
        <>
          <hr />
          <p className="notification__details">{details}</p>
        </>
      )}
    </>
  );
}

Компонент-обёртка без DOM-узлов

// Провайдер без лишнего узла в DOM
function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider>
      <AuthProvider>
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      </AuthProvider>
    </ThemeProvider>
  );
}

// Использование в корне приложения
root.render(
  <AppProviders>
    <App />
  </AppProviders>
);

Fragment vs div: когда что использовать

Ситуация Fragment <> <div>
Нет CSS-стилей или обработчиков ✅ Предпочтительно ❌ Лишний DOM-узел
Нужна семантика таблицы/списка ✅ Обязательно ❌ Нарушает структуру
Нужен CSS класс / id ❌ Не поддерживает
Нужен обработчик событий ❌ Не поддерживает
Нужен key в списке <Fragment key> <div key>
Flex / Grid контейнер

Проверка в DevTools

В React DevTools Fragment отображается как специальный узел, не создающий DOM-элемент:

App
  └─ Fragment          ← Виден в DevTools, но не в DOM
       ├─ <h1>
       ├─ <p>
       └─ <p>

В реальном DOM будет:

<h1>...</h1>
<p>...</p>
<p>...</p>

Краткое резюме

Синтаксис Пример Когда использовать
<Fragment> <Fragment>...</Fragment> Нужен атрибут key
<> <>...</> Большинство случаев

Дополнительные материалы

Стрелочка влевоПорталы в React: рендер компонентов вне иерархии DOM

Постройте личный план изучения React до уровня Middle — бесплатно!

React — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React

Uncontrolled Components: когда DOM управляет даннымиRender Props: гибкое управление рендерингом в ReactПрофилирование React: как найти и устранить узкие местаЛенивая загрузка: как ускорить React-приложение в разыЧастичное применение: как создавать компоненты без лишнего кодаHOC в React: мастерство композиции компонентовuseMemo: как спасти производительность от тяжелых вычисленийError Boundaries: создаем надежные React-приложенияКонтролируемые компоненты в React: полный контроль над формамиCompound Components в React: создаем гибкие компоненты с мощным APIКомпозиция компонентов в React: строим гибкие интерфейсыCode Splitting в React: как уменьшить бандл и ускорить загрузку приложенияАсинхронные компоненты в React: новый стандарт работы с данными
useState в React что это и как использоватьuseTransition - плавные переходы между состояниямиuseSyncExternalStore — работа с внешними сторамиuseRef в React — создание ссылок на DOM и значенияuseLayoutEffect в React — эффект до отрисовкиuseInsertionEffect — внедрение стилей до мутаций DOMuseImperativeHandle в React — настройка ref дочернего компонентаuseId — генерация уникальных идентификаторовuseDeferredValue — отложенное обновление состоянияuseDebugValue — отладка кастомных хуковuseCallback в React — мемоизация функцийuseReducer — альтернатива useState для сложной логикиuseMemo в React: как и когда оптимизировать тяжелые вычисленияuseEffect в React что это и как использоватьuseContext — работа с контекстом в ReactОптимизация рендеринга в React: от теории к глубокой практикеЧто такое useRef и как его применять в ReactКак и зачем использовать React HooksУправление состоянием в React через ContextКак предотвратить лишние ре-рендеры в React: полное руководствоuseMemo vs useCallback: подробное руководство по мемоизации в ReactПравила хуков — правила использованияuseEffect vs useLayoutEffect: в чём разница и какой хук выбрать?Кастомные хуки в React — создание собственных хуковuseState продвинутое использование в React
StrictMode в React — как находить ошибки на этапе разработкиСерверные компоненты React (RSC) — подробный разбор и практикаКак работает рендеринг в ReactЧто такое props в React и как их правильно использоватьКак работает JSX связка React и HTMLЧто такое React.js и как его использоватьКак использовать элементы в ReactКак использовать React DOM в проектеЧто такое компоненты в React и как их применятьРабота с children в ReactПорталы в React: рендер компонентов вне иерархии DOMFragment в React: группировка элементов без лишних узлов DOM
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React и Redux Toolkit

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий