JSX не является нативным синтаксисом JavaScript. Он преобразуется в вызовы функций во время сборки, и именно здесь появляется понятие JSX pragma — инструкции, определяющей, какая функция должна использоваться для создания элементов.
По умолчанию классический JSX трансформируется в:
React.createElement("div", null, "text");
Однако эта функция не является обязательной. JSX может компилироваться в любые вызовы, если задать соответствующий pragma.
Любой JSX-код:
const element = <h1>Hello</h1>;
после трансформации становится функцией:
const element = h("h1", null, "Hello");
Имя функции h, React.createElement или
любое другое определяется настройками трансформации.
JSX pragma — это способ явно указать трансформеру, какую функцию использовать.
Исторически JSX pragma задавался прямо в файле через комментарий:
/** @jsx h */
После этого JSX компилируется в вызовы h():
/** @jsx h */
const title = <h1>Hello</h1>;
Результат:
const title = h("h1", null, "Hello");
Этот подход до сих пор поддерживается в некоторых трансформерах, но считается устаревшим в современных сборках.
В сборщике Parcel JSX трансформация выполняется через Babel или встроенные трансформеры (в зависимости от конфигурации проекта). Parcel не требует ручной настройки JSX pragma в большинстве стандартных React-проектов, однако поддерживает его при кастомных конфигурациях.
Parcel автоматически определяет среду и применяет React JSX transform:
Разница принципиальна:
React.createElement("div", null);
Требует:
import React from "react";
или явно заданного pragma.
import { jsx } from "react/jsx-runtime";
jsx("div", {});
React импортируется автоматически трансформером.
Parcel позволяет подключать Babel-конфигурацию через
.babelrc или babel.config.json.
Пример настройки custom pragma:
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"pragma": "h",
"pragmaFrag": "Fragment"
}]
]
}
После этого JSX:
const app = <div>text</div>;
преобразуется в:
const app = h("div", null, "text");
Фрагменты также можно переназначить:
{
"pragmaFrag": "Fragment"
}
или даже:
{
"pragmaFrag": "h.Fragment"
}
Если используется TypeScript, JSX pragma управляется через
tsconfig.json.
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
}
}
{
"compilerOptions": {
"jsx": "react-jsx"
}
}
или
{
"jsx": "react-jsxdev"
}
В этом режиме pragma не требуется, поскольку используется автоматический runtime.
JSX pragma особенно важен при работе с альтернативными библиотеками.
Для Preact часто используется:
{
"pragma": "h",
"pragmaFrag": "Fragment"
}
или через Babel:
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"pragma": "h",
"pragmaFrag": "Fragment"
}]
]
}
Emotion использует собственный JSX runtime:
{
"plugins": [
["@emotion/babel-plugin-jsx-pragmatic", {
"export": "jsx",
"import": "__css",
"module": "@emotion/react"
}]
]
}
После этого JSX начинает учитывать стилизации на этапе трансформации.
Современные версии React и Parcel ориентированы на автоматический runtime, где JSX превращается в:
import { jsx as _jsx } from "react/jsx-runtime";
и pragma больше не нужен.
Для включения:
{
"compilerOptions": {
"jsx": "react-jsx"
}
}
или Babel:
{
"runtime": "automatic"
}
Parcel сам по себе не требует прямой настройки pragma, но его поведение зависит от следующих факторов:
Parcel выполняет:
При отсутствии пользовательских настроек используется дефолтная стратегия React automatic runtime.
| Стратегия | JSX трансформация | Требует React import | Современность |
|---|---|---|---|
| Classic pragma | React.createElement | да | устаревшая |
| Custom pragma | h() / custom fn | зависит | средняя |
| Automatic runtime | jsx() из runtime | нет | современная |
Если установлен automatic runtime, но Babel настроен на
pragma, возникает конфликт:
Если в tsconfig.json стоит:
"jsx": "react-jsx"
а Babel задаёт:
"pragma": "h"
TypeScript уже трансформирует JSX до Babel, и pragma игнорируется.
При использовании classic режима требуется:
import React from "react";
иначе код не соберётся, даже если JSX pragma задан.
Parcel позволяет использовать гибридные конфигурации:
Такой подход применяется в библиотеках UI-фреймворков и design systems, где требуется полный контроль над JSX деревом.
Выбор стратегии зависит от архитектуры:
Parcel не ограничивает выбор, а лишь исполняет выбранную цепочку трансформаций через подключённые инструменты.