@json-to-jsx/engine
v0.1.0
Published
JSON-to-JSX no-code engine built on Mantine UI
Maintainers
Readme
@repo/schema-engine
Також доступно: English 🇬🇧
JSON-to-JSX рушій без коду на базі Mantine UI.
Опишіть UI як JSON-схему → рендеринг у реальному часі → експорт готового .tsx файлу.
Запит користувача ──▶ AI (Gemini / OpenAI / Anthropic) ──▶ JSON Schema
▲ │
База знань ▼
(правила категорій SchemaRenderer (live preview)
+ приклади) │
▼
generateTsx()
│
▼
Завантажити .tsx файлЗміст
- Встановлення
- Швидкий старт
- Специфікація схеми
- API Reference
- Генератор коду
- Власні компоненти
- Інтернаціоналізація
- AI Інтеграція
- Кастомізація теми
- Playground
- Карта експортів
- Приклади
Встановлення
# pnpm
pnpm add @repo/schema-engine @mantine/core @mantine/hooks
# npm
npm install @repo/schema-engine @mantine/core @mantine/hooksPeer dependencies: react >= 18, @mantine/core >= 7
Обгорніть застосунок у MantineProvider та підключіть стилі Mantine:
import "@mantine/core/styles.css";
import { MantineProvider } from "@mantine/core";
export default function App({ children }) {
return <MantineProvider>{children}</MantineProvider>;
}Швидкий старт
import { SchemaRenderer, SchemaEngineProvider } from "@repo/schema-engine";
import type { Schema } from "@repo/schema-engine";
const schema: Schema = {
version: "1.0",
meta: { title: "Форма входу", locale: "uk" },
root: {
type: "Stack",
props: { gap: "md", maw: 400, mx: "auto" },
children: [
{ type: "Title", props: { order: 2 }, children: "Увійти" },
{ type: "TextInput", props: { label: "Email", required: true } },
{ type: "PasswordInput", props: { label: "Пароль", required: true } },
{
type: "Button",
props: { fullWidth: true, onClick: "handleSubmit" },
children: "Увійти",
},
],
},
};
export function App() {
return (
<SchemaEngineProvider>
<SchemaRenderer
schema={schema}
locale="uk"
callbacks={{ handleSubmit: () => console.log("відправлено") }}
/>
</SchemaEngineProvider>
);
}Специфікація схеми
Коренева схема
interface Schema {
version: "1.0"; // обов'язково, має бути рівно "1.0"
root: SchemaNode; // кореневий вузол дерева компонентів
meta?: {
title?: string; // використовується як ім'я компонента при кодогенерації
description?: string;
locale?: string; // локаль за замовчуванням для $i18n (напр. "uk")
theme?: SchemaTheme; // переопределення теми Mantine
};
}Структура вузла
interface SchemaNode {
type: string; // назва компонента з реєстру
props?: Record<string, unknown>; // пропси Mantine компонента
children?: TextValue | SchemaNode | SchemaNode[]; // вкладений контент
style?: React.CSSProperties; // інлайн стилі
visible?: boolean; // false = не рендерити (за замовч.: true)
key?: string; // React key
}TextValue
Будь-який пропс або children з текстом може бути TextValue:
| Формат | Приклад | Коли використовувати |
|--------|---------|----------------------|
| Рядок | "Привіт" | Статичний контент |
| { "$t": "ключ" } | { "$t": "nav.home" } | Зовнішня i18n (next-intl, i18next…) |
| { "$i18n": { "en": "…", "uk": "…" } } | — | Вбудовані переклади прямо в схемі |
Застосовується до: children, label, placeholder, description, error, title, cite.
{
"type": "TextInput",
"props": {
"label": { "$i18n": { "en": "Email address", "uk": "Електронна пошта" } },
"placeholder": { "$t": "form.email_placeholder" }
}
}EventHandler
Події (onClick, onChange, onSubmit, onClose, onOpen, onFocus, onBlur, onStepClick) підтримують два формати:
{ "onClick": "handleSubmit" }{ "onClick": { "$action": "submitForm", "payload": { "formId": "login" } } }Рядок-ключ шукається в об'єкті callbacks переданому до <SchemaRenderer>.
Формат $action викликає callbacks[action](payload).
Доступні типи компонентів
Розмітка (Layout)
Stack · Group · Grid · GridCol · Container · Center · Flex · SimpleGrid · Space · Divider · Box · Paper · ScrollArea · AspectRatio
Форми (Forms)
TextInput · PasswordInput · NumberInput · Textarea · Select · MultiSelect · Checkbox · CheckboxGroup · RadioGroup · Radio · Switch · Slider · RangeSlider · FileInput · ColorInput · ColorPicker · PinInput · Rating · SegmentedControl · Chip · ChipGroup
Контент (Content)
Text · Title · Image · Anchor · List · ListItem · Blockquote · Code · Highlight · Mark · Badge · Alert · Notification · ThemeIcon · Kbd · Spoiler
Інтерактивні (Interactive)
Button · ActionIcon · CloseButton · UnstyledButton · Modal · Drawer · Tabs · TabsList · TabsTab · TabsPanel · Accordion · AccordionItem · AccordionControl · AccordionPanel · Menu · MenuTarget · MenuDropdown · MenuItem · Tooltip · Popover · PopoverTarget · PopoverDropdown · HoverCard · HoverCardTarget · HoverCardDropdown · Dialog · LoadingOverlay · Overlay · Collapse
Відображення даних (Data Display)
Table · TableThead · TableTbody · TableTr · TableTh · TableTd · Card · CardSection · Timeline · TimelineItem · Stepper · StepperStep · Avatar · AvatarGroup · Indicator · Progress · ProgressSection · RingProgress · BackgroundImage · ColorSwatch · Skeleton · NumberFormatter
Навігація (Navigation)
NavLink · Breadcrumbs · Pagination · Burger
Примітка щодо складових компонентів: в схемі використовуйте плоскі імена (
GridCol,TabsList,TableTr). Рендерер та генератор коду автоматично конвертують їх у крапкову нотацію Mantine (Grid.Col,Tabs.List,Table.Tr).
Правила ієрархії
| Правило | Деталі |
|---------|--------|
| GridCol | Має бути прямим дочірнім елементом Grid |
| TabsList, TabsTab, TabsPanel | Мають бути всередині Tabs; TabsTab/TabsPanel вимагають проп value; Tabs вимагає defaultValue |
| AccordionItem | Має бути всередині Accordion; вимагає проп value |
| AccordionControl, AccordionPanel | Мають бути всередині AccordionItem |
| MenuTarget, MenuDropdown | Мають бути всередині Menu |
| PopoverTarget, PopoverDropdown | Мають бути всередині Popover |
| Modal, Drawer | Вимагають булевий проп opened та обробник onClose |
| StepperStep | Має бути всередині Stepper |
| TableTr | Має бути всередині TableThead або TableTbody |
API Reference
SchemaRenderer
import { SchemaRenderer } from "@repo/schema-engine";
<SchemaRenderer
schema={schema} // Schema | SchemaNode — обов'язково
registry={registry} // ComponentRegistry — за замовч. createDefaultRegistry()
locale="uk" // активна локаль для $i18n
fallbackLocale="en" // запасна локаль якщо переклад відсутній (за замовч. "en")
t={(key) => i18n.t(key)} // зовнішня i18n функція для $t ключів
callbacks={{ // карта обробників подій
handleSubmit: () => {},
handleCancel: () => {},
}}
onError={(error, node) => ( // кастомний рендерер помилок per-node
<Alert color="red">{error.message}</Alert>
)}
/>| Проп | Тип | Обов'язк. | Опис |
|------|-----|-----------|------|
| schema | Schema \| SchemaNode | ✓ | Схема або вузол для рендерингу |
| registry | ComponentRegistry | — | Реєстр компонентів |
| locale | string | — | Активна локаль (напр. "uk", "en") |
| fallbackLocale | string | — | Запасна локаль (за замовч. "en") |
| t | (key: string) => string | — | Функція для { $t } значень |
| callbacks | Record<string, Function> | — | Іменовані обробники подій |
| onError | (error, node) => ReactNode | — | Кастомна межа помилок per-node |
SchemaEngineProvider
Обгортає MantineProvider та застосовує опціональні токени теми зі схеми.
import { SchemaEngineProvider } from "@repo/schema-engine";
<SchemaEngineProvider theme={{ primaryColor: "violet", defaultRadius: "md" }}>
<SchemaRenderer schema={schema} />
</SchemaEngineProvider>Якщо у вашому застосунку вже є MantineProvider, можна використовувати SchemaRenderer напряму без SchemaEngineProvider.
validateSchema
import { validateSchema } from "@repo/schema-engine";
const result = validateSchema(unknownJson);
if (result.success) {
const schema: Schema = result.data;
} else {
console.error(result.errors.format()); // ZodError
}У режимі розробки (NODE_ENV !== "production") SchemaRenderer автоматично валідує та виводить попередження в консоль.
ComponentRegistry
import { createRegistry, createDefaultRegistry } from "@repo/schema-engine";
const registry = createDefaultRegistry(); // всі 60+ Mantine компонентів
registry.register("MyCard", MyCardComponent);
registry.unregister("Burger");
registry.get("MyCard"); // ComponentType | undefined
registry.has("MyCard"); // boolean
registry.getAll(); // Map<string, ComponentType>Генератор коду
Перетворює Schema у готовий .tsx файл з коректними Mantine імпортами.
generateTsx
import { generateTsx } from "@repo/schema-engine/codegen";
const code = generateTsx(schema, {
componentName: "LoginForm", // за замовч.: береться з schema.meta.title
exportType: "named", // "named" | "default" (за замовч.: "named")
});Результат генерації:
"use client";
import { Button, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core";
export function LoginForm() {
return (
<Stack gap="md" maw={400} mx="auto">
<Title order={2} ta="center">Sign In</Title>
<Paper withBorder shadow="md" p="xl" radius="md">
<Stack gap="sm">
<TextInput label="Email" placeholder="[email protected]" required />
<PasswordInput label="Password" placeholder="Your password" required />
<Button fullWidth mt="md" type="submit" onClick={() => { /* handleSubmit */ }}>Sign in</Button>
</Stack>
</Paper>
</Stack>
);
}| Опція | Тип | За замовч. | Опис |
|-------|-----|------------|------|
| componentName | string | З meta.title | Ім'я React функції |
| exportType | "named" \| "default" | "named" | Тип експорту |
collectImports
Рекурсивно збирає всі Mantine імпорти для дерева вузлів:
import { collectImports, buildImportStatements } from "@repo/schema-engine/codegen";
const imports = collectImports(schema.root);
// Map { "@mantine/core" => Set { "Button", "Stack", "TextInput" } }
const statement = buildImportStatements(imports);
// 'import { Button, Stack, TextInput } from "@mantine/core";'serializeNode
Конвертує SchemaNode у JSX рядок:
import { serializeNode } from "@repo/schema-engine/codegen";
serializeNode({ type: "TextInput", props: { label: "Email", required: true } }, 4);
// ' <TextInput label="Email" required />'
serializeNode({ type: "GridCol", props: { span: 6 } }, 4);
// ' <Grid.Col span={6} />'Власні компоненти
import { createDefaultRegistry, SchemaRenderer } from "@repo/schema-engine";
const registry = createDefaultRegistry();
function KpiCard({ title, value }: { title: string; value: string }) {
return <div><span>{title}</span><strong>{value}</strong></div>;
}
registry.register("KpiCard", KpiCard as React.ComponentType<Record<string, unknown>>);
const schema = {
version: "1.0",
root: { type: "KpiCard", props: { title: "Виручка", value: "$84,220" } },
};
<SchemaRenderer schema={schema} registry={registry} />Інтернаціоналізація
Рушій не залежить від конкретної i18n бібліотеки і підтримує три підходи.
1. Зовнішня i18n ($t)
import { useTranslations } from "next-intl";
import { SchemaRenderer } from "@repo/schema-engine";
function Page({ schema }) {
const t = useTranslations();
return <SchemaRenderer schema={schema} t={t} locale="uk" />;
}{
"type": "TextInput",
"props": {
"label": { "$t": "form.email_label" }
}
}2. Вбудовані переклади ($i18n)
Самодостатньо — жодна зовнішня бібліотека не потрібна:
{
"version": "1.0",
"meta": { "locale": "uk" },
"root": {
"type": "Button",
"children": { "$i18n": { "en": "Submit", "uk": "Надіслати", "de": "Absenden" } }
}
}<SchemaRenderer schema={schema} locale={currentLocale} />3. Прості рядки
Для статичного контенту — просто використовуйте звичайні рядки. Ніяких налаштувань не потрібно.
AI Інтеграція
Вбудовані провайдери
Три AI провайдери вже вбудовані. Всі реалізують один інтерфейс AIProvider:
import { GeminiProvider, OpenAIProvider, AnthropicProvider } from "@repo/schema-engine/ai";
import type { AIProvider } from "@repo/schema-engine/ai";
const provider: AIProvider = new GeminiProvider({
apiKey: process.env.GEMINI_API_KEY!,
// model: "gemini-2.5-flash", // за замовч.
// temperature: 0.3,
// maxTokens: 4096,
});
const raw = await provider.generate(systemPrompt, userPrompt);| Провайдер | Модель за замовч. | Змінна середовища |
|-----------|-------------------|-------------------|
| GeminiProvider | gemini-2.5-flash | GEMINI_API_KEY |
| OpenAIProvider | gpt-4o-mini | OPENAI_API_KEY |
| AnthropicProvider | claude-opus-4-6 | ANTHROPIC_API_KEY |
Інтерфейс AIProvider:
interface AIProvider {
name: string;
generate(systemPrompt: string, userPrompt: string): Promise<string>;
}База знань
База знань надає категорійно-специфічні правила та приклади схем, що вводяться в системний промпт AI залежно від ключових слів запиту користувача.
9 категорій: auth, form, dashboard, data-display, navigation, ecommerce, content, feedback, layout
import { matchKnowledge, buildContextFromKnowledge } from "@repo/schema-engine/knowledge";
const entries = matchKnowledge("форма входу з email і паролем");
// [{ category: "auth", rules: [...], componentHints: [...], examples: [...] }]
const context = buildContextFromKnowledge(entries);
// Форматований рядок для додавання в системний промпт AImatchKnowledge(prompt, topN?) — оцінює кожну категорію за збігом ключових слів, повертає top N (за замовч.: 3). Повертає layout якщо жодного збігу немає.
buildEnhancedPrompt
Поєднує базовий системний промпт + контекст бази знань + підказку локалі в готову пару промптів:
import { buildEnhancedPrompt, parseAIResponse } from "@repo/schema-engine/ai";
const { systemPrompt, userMessage } = buildEnhancedPrompt(
"Сторінка налаштувань з вкладками профілю і сповіщень",
{ locale: "uk" }
);
// Використовуємо з будь-яким провайдером:
const raw = await provider.generate(systemPrompt, userMessage);
const schema = parseAIResponse(raw); // Schema | nullparseAIResponse
Витягує та валідує Schema з відповіді AI (обробляє JSON у блоках markdown):
import { parseAIResponse } from "@repo/schema-engine/ai";
const schema = parseAIResponse(aiText); // Schema | nullПовний приклад AI генерації
import { GeminiProvider, buildEnhancedPrompt, parseAIResponse } from "@repo/schema-engine/ai";
import { generateTsx } from "@repo/schema-engine/codegen";
const provider = new GeminiProvider({ apiKey: process.env.GEMINI_API_KEY! });
async function promptToTsx(userPrompt: string): Promise<string | null> {
const { systemPrompt, userMessage } = buildEnhancedPrompt(userPrompt, { locale: "uk" });
const raw = await provider.generate(systemPrompt, userMessage);
const schema = parseAIResponse(raw);
if (!schema) return null;
return generateTsx(schema);
}
const tsx = await promptToTsx("Форма реєстрації з ім'ям, email, паролем і вибором країни");
// → готовий вміст .tsx файлуКастомізація теми
{
"version": "1.0",
"meta": {
"theme": {
"primaryColor": "violet",
"defaultRadius": "md",
"fontFamily": "Inter, sans-serif"
}
},
"root": { "type": "Stack", "children": [] }
}interface SchemaTheme {
colorScheme?: "light" | "dark" | "auto";
primaryColor?: string;
colors?: Record<string, string[]>;
fontFamily?: string;
headings?: { fontFamily?: string };
defaultRadius?: string;
spacing?: Record<string, string>;
other?: Record<string, unknown>;
}Playground
Інтерактивний playground за адресою apps/web/app/playground надає:
- JSON Editor — пишіть або вставляйте схему, бачте живий Mantine preview
- AI Prompt — описуйте UI природною мовою → Gemini/OpenAI/Anthropic генерує схему
- Code вкладка — синтаксично підсвічений
.tsxвивід, копіювання в буфер або завантаження файлу - Вибір провайдера — перемикайтесь між Gemini, OpenAI, Anthropic у тулбарі
Запуск локально:
# 1. Задайте API ключ
echo "GEMINI_API_KEY=ваш_ключ" >> apps/web/.env.local
# 2. Запустіть dev сервер
pnpm devВідкрийте http://localhost:3000/playground.
Карта експортів
| Шлях імпорту | Що отримуєте |
|--------------|--------------|
| @repo/schema-engine | SchemaRenderer, SchemaEngineProvider, validateSchema, createDefaultRegistry, всі типи |
| @repo/schema-engine/schema | validateSchema, schemaNodeSchema, типи схеми |
| @repo/schema-engine/registry | ComponentRegistry, createRegistry, createDefaultRegistry, registerAll |
| @repo/schema-engine/ai | buildEnhancedPrompt, parseAIResponse, getSystemPrompt, GeminiProvider, OpenAIProvider, AnthropicProvider, тип AIProvider |
| @repo/schema-engine/codegen | generateTsx, collectImports, buildImportStatements, serializeNode, COMPONENT_TO_IMPORT |
| @repo/schema-engine/knowledge | matchKnowledge, buildContextFromKnowledge, KNOWLEDGE_BASE |
Приклади
Playground містить 7 готових схем:
| Приклад | Опис |
|---------|------|
| Login Form | Email + пароль + кнопка входу |
| Registration Form | Повна форма реєстрації з усіма типами полів |
| Dashboard Layout | SimpleGrid картки статистики + Table з бейджами |
| Settings Page | Tabs → панелі Профіль / Безпека / Сповіщення |
| Product Card | Card із зображенням, ціною, бейджем і CTA |
| i18n Example | Вбудований $i18n з перемикачем локалі |
| Nested Layout | Глибоке вкладення Grid → Stack → Paper → Stack |
Ліцензія
MIT © 2026
