
JSX a TSX
Syntaktické rozšíření JavaScriptu a TypeScriptu pro psaní HTML-like kódu přímo v JS souborech.
JSX (JavaScript XML) je syntaktické rozšíření JavaScriptu, které umožňuje psát HTML-like kód přímo v JavaScript souborech. TSX je totéž pro TypeScript. Popularitu získalo díky knihovně React, ale dnes ho používají i další frameworky.
Co je JSX
JSX vypadá jako HTML, ale ve skutečnosti se kompiluje do volání JavaScriptových funkcí:
// JSX zápis
const element = <h1 className="title">Ahoj světe</h1>;
// Kompiluje se do
const element = React.createElement(
'h1',
{ className: 'title' },
'Ahoj světe'
);
Prohlížeč JSX nerozumí — potřebujete nástroj jako Babel, TypeScript nebo esbuild, který JSX převede do běžného JavaScriptu.
Rozdíl mezi JSX a TSX
Jediný rozdíl je v příponě souboru a typové kontrole:
.jsx— JavaScript s JSX syntaxí.tsx— TypeScript s JSX syntaxí
V TSX máte navíc typovou kontrolu props:
// TSX s typováním
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
function Button({ label, onClick, disabled }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}
Základní syntaxe
Elementy
JSX elementy vypadají jako HTML, ale s drobnými rozdíly:
// Jeden element
const heading = <h1>Nadpis</h1>;
// Vnořené elementy
const card = (
<div className="card">
<h2>Titulek</h2>
<p>Obsah karty</p>
</div>
);
// Self-closing tagy
const image = <img src="/foto.jpg" alt="Popis" />;
const input = <input type="text" />;
Fragmenty
JSX vyžaduje jeden kořenový element. Pro více elementů bez wrapperu použijte fragment:
// Fragment pomocí <>...</>
function List() {
return (
<>
<li>První</li>
<li>Druhý</li>
<li>Třetí</li>
</>
);
}
// Nebo React.Fragment s key
function Items({ items }) {
return items.map(item => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.definition}</dd>
</React.Fragment>
));
}
Výrazy v JSX
Složené závorky {} umožňují vkládat JavaScript výrazy:
const name = 'Jan';
const count = 5;
// Proměnné
const greeting = <p>Ahoj, {name}!</p>;
// Výrazy
const doubled = <span>{count * 2}</span>;
// Volání funkcí
const upper = <p>{name.toUpperCase()}</p>;
// Template literály
const message = <p>{`Máte ${count} zpráv`}</p>;
Podmíněné vykreslování
// Ternární operátor
function Status({ isOnline }) {
return (
<span>
{isOnline ? 'Online' : 'Offline'}
</span>
);
}
// Logické AND pro podmíněné zobrazení
function Notification({ count }) {
return (
<div>
{count > 0 && <span className="badge">{count}</span>}
</div>
);
}
// Podmínka mimo JSX
function Message({ type }) {
let icon;
if (type === 'error') {
icon = <ErrorIcon />;
} else if (type === 'warning') {
icon = <WarningIcon />;
} else {
icon = <InfoIcon />;
}
return <div>{icon}</div>;
}
Iterace
const items = ['Jablko', 'Hruška', 'Banán'];
function FruitList() {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
// S objekty
const users = [
{ id: 1, name: 'Jan' },
{ id: 2, name: 'Eva' }
];
function UserList() {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Atribut key je povinný při vykreslování seznamů. Pomáhá Reactu identifikovat, které položky se změnily. Použijte unikátní ID z dat — index jako key způsobuje problémy při přeřazování položek.
Atributy a props
HTML atributy
Některé HTML atributy mají v JSX jiný název. Důvody jsou dva:
Vyhrazená slova v JavaScriptu — class a for jsou klíčová slova JS:
| HTML | JSX |
|---|---|
class |
className |
for |
htmlFor |
Konsistence s DOM API — JSX používá camelCase jako nativní DOM vlastnosti:
| HTML | JSX / DOM |
|---|---|
tabindex |
tabIndex |
readonly |
readOnly |
colspan |
colSpan |
<label htmlFor="email" className="form-label">
E-mail
</label>
<input
id="email"
type="email"
className="form-input"
readOnly={false}
tabIndex={0}
/>
Spread atributy
Pomocí spread operátoru můžete předat objekt jako props:
const buttonProps = {
type: 'submit',
className: 'btn btn-primary',
disabled: false
};
// Všechny props najednou
<button {...buttonProps}>Odeslat</button>
// Kombinace se specifickými props
<button {...buttonProps} onClick={handleClick}>
Odeslat
</button>
Inline styly
Atribut style přijímá objekt, ne řetězec:
// Správně - objekt
<div style={{ color: 'red', fontSize: '16px' }}>
Text
</div>
// CSS vlastnosti jsou camelCase
const styles = {
backgroundColor: '#f0f0f0',
borderRadius: '8px',
padding: '1rem',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
};
<div style={styles}>Karta</div>
// Číselné hodnoty (px se přidá automaticky pro některé vlastnosti)
<div style={{ width: 200, height: 100 }}>Box</div>
Události
Události v JSX jsou camelCase a přijímají funkci jako handler:
function Button() {
const handleClick = (event) => {
console.log('Kliknuto!', event);
};
return (
<button onClick={handleClick}>
Klikni
</button>
);
}
// Inline handler
<button onClick={() => console.log('Klik')}>
Tlačítko
</button>
// S parametrem
<button onClick={() => handleDelete(item.id)}>
Smazat
</button>
// Různé události
<input
onChange={(e) => setValue(e.target.value)}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
onKeyDown={(e) => e.key === 'Enter' && submit()}
/>
<form onSubmit={(e) => {
e.preventDefault();
handleSubmit();
}}>
...
</form>
Komponenty
Komponenty jsou funkce vracející JSX. Názvy začínají velkým písmenem:
// Jednoduchá komponenta
function Welcome() {
return <h1>Vítejte!</h1>;
}
// Komponenta s props
function Greeting({ name, age }) {
return (
<div>
<h2>Ahoj, {name}!</h2>
<p>Je ti {age} let.</p>
</div>
);
}
// Použití
function App() {
return (
<div>
<Welcome />
<Greeting name="Jan" age={25} />
</div>
);
}
Children
Obsah mezi otevíracím a zavíracím tagem je dostupný jako children:
function Card({ title, children }) {
return (
<div className="card">
<h3>{title}</h3>
<div className="card-body">
{children}
</div>
</div>
);
}
// Použití
<Card title="Můj článek">
<p>Obsah karty...</p>
<button>Akce</button>
</Card>
TSX a typování
V TypeScriptu definujete typy pro props:
// Rozhraní pro props
interface UserCardProps {
name: string;
email: string;
avatar?: string; // volitelný
onEdit: (id: number) => void;
}
function UserCard({ name, email, avatar, onEdit }: UserCardProps) {
return (
<div className="user-card">
{avatar && <img src={avatar} alt={name} />}
<h3>{name}</h3>
<p>{email}</p>
<button onClick={() => onEdit(1)}>Upravit</button>
</div>
);
}
// S generiky
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}
Vestavěné typy
import { ReactNode, MouseEvent, ChangeEvent } from 'react';
interface Props {
// Cokoliv vykreslitelného
children: ReactNode;
// Event handlery
onClick: (e: MouseEvent<HTMLButtonElement>) => void;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
}
// Komponenta rozšiřující HTML atributy
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant: 'primary' | 'secondary';
}
function Button({ variant, children, ...rest }: ButtonProps) {
return (
<button className={`btn btn-${variant}`} {...rest}>
{children}
</button>
);
}
Jak JSX funguje
JSX se transformuje do volání funkcí. Starší způsob používal React.createElement:
// JSX
<div className="container">
<h1>Nadpis</h1>
<p>Text</p>
</div>
// Starý transform (před React 17)
React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, 'Nadpis'),
React.createElement('p', null, 'Text')
);
Od React 17 existuje nový JSX transform, který nevyžaduje import Reactu:
// Nový transform (React 17+)
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime';
_jsxs('div', {
className: 'container',
children: [
_jsx('h1', { children: 'Nadpis' }),
_jsx('p', { children: 'Text' })
]
});
AST – Abstract Syntax Tree
Abstract Syntax Tree (abstraktní syntaktický strom) je stromová reprezentace zdrojového kódu. Každý uzel stromu představuje konstrukci v kódu. Kompilátory a transpilery používají AST pro analýzu a transformaci kódu.
Jak vzniká AST
Zdrojový kód → Lexer → Tokeny → Parser → AST → Transformer → Výstup
1. Lexer (tokenizer) rozdělí kód na tokeny:
const x = 1 + 2;
// Tokeny:
[CONST, IDENTIFIER("x"), EQUALS, NUMBER(1), PLUS, NUMBER(2), SEMICOLON]
2. Parser sestaví strom podle gramatiky jazyka:
// Jednoduchý příklad AST
const x = 1 + 2;
// AST (zjednodušeně):
Program
└── VariableDeclaration (const)
└── VariableDeclarator
├── Identifier (name: "x")
└── BinaryExpression (operator: "+")
├── NumericLiteral (value: 1)
└── NumericLiteral (value: 2)
JSX v AST
JSX elementy mají vlastní typy uzlů v AST:
<div className="box">Hello</div>
// AST:
JSXElement
├── JSXOpeningElement
│ ├── JSXIdentifier (name: "div")
│ └── JSXAttribute
│ ├── JSXIdentifier (name: "className")
│ └── StringLiteral (value: "box")
├── JSXText (value: "Hello")
└── JSXClosingElement
└── JSXIdentifier (name: "div")
3. Transformer převede JSX uzly na volání funkcí:
// JSX AST uzel se transformuje na:
CallExpression
├── callee: MemberExpression (React.createElement)
└── arguments:
├── StringLiteral ("div")
├── ObjectExpression ({ className: "box" })
└── StringLiteral ("Hello")
AST si můžete prohlédnout na astexplorer.net — zadáte kód a vidíte strom v reálném čase.
Nástroje pro transformaci
| Nástroj | Jazyk | Rychlost |
|---|---|---|
| Babel | JavaScript | Pomalý |
| TypeScript | TypeScript | Střední |
| esbuild | Go | Rychlý |
| SWC | Rust | Rychlý |
Historie JSX
2004 — E4X (předchůdce)
ECMAScript for XML byl standardizovaný způsob psaní XML v JavaScriptu. Byl součástí Firefoxu 10–20, ale nikdy se neprosadil:
// E4X (dnes mrtvé)
var person = <person>
<name>Jan</name>
<age>25</age>
</person>;
var name = person.name; // "Jan"
2013 — Facebook vytváří JSX
Jordan Walke a tým ve Facebooku vytvořili JSX pro React. Motivace:
React.createElement()byl příliš verbose- Stringové šablony (Mustache, Handlebars) neměly typovou kontrolu
- HTML-like syntaxe je intuitivní pro UI
// Před JSX (2013)
React.createElement('div', { className: 'box' },
React.createElement('h1', null, 'Title'),
React.createElement('p', null, 'Text')
);
// S JSX — mnohem čitelnější
<div className="box">
<h1>Title</h1>
<p>Text</p>
</div>
Facebook publikoval JSX specifikaci jako nezávislý standard. JSX není vázané na React.
2014 — Babel
Sebastian McKenzie vytvořil 6to5 (později přejmenovaný na Babel), který přinesl snadnou kompilaci JSX pro všechny projekty.
2015 — TypeScript 1.6 přidává TSX
Microsoft přidal podporu .tsx souborů s plnou typovou kontrolou props:
interface Props {
name: string;
}
function Hello({ name }: Props) {
return <h1>Hello {name}</h1>;
}
// Chyba: Property 'name' is missing
<Hello />
2017 — Fragment syntax
React 16.2 přidal <>...</> jako zkratku pro React.Fragment:
// Před
<React.Fragment>
<li>A</li>
<li>B</li>
</React.Fragment>
// Po
<>
<li>A</li>
<li>B</li>
</>
2020 — Nový JSX Transform (React 17)
Změna z React.createElement na automatický import z react/jsx-runtime. Výhody:
- Není potřeba
import Reactv každém souboru - Menší bundle size
- Lepší výkon
Dnes
JSX používá mnoho frameworků:
- React — původní implementace
- Preact — lehká alternativa (3 KB)
- SolidJS — kompiluje JSX přímo do DOM operací
- Qwik — framework s okamžitým obnovením stavu (HTML obsahuje serialisovaný stav, JS se načítá lazy)
- Vue — volitelně s pluginem
Nastavení projektu
Vite (doporučeno)
# Nový projekt s React + TypeScript
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
TypeScript konfigurace
Pro TSX soubory nastavte v tsconfig.json:
{
"compilerOptions": {
"jsx": "react-jsx", // Pro React 17+
"strict": true,
"esModuleInterop": true
}
}
Možnosti pro jsx:
react— starý transform, vyžadujeimport Reactreact-jsx— nový transform (React 17+)react-jsxdev— nový transform s debug infopreserve— ponechá JSX pro další nástroj
Pravidla JSX
- Jeden kořenový element — nebo fragment
- Všechny tagy musí být zavřené —
<br />, ne<br> - camelCase pro atributy —
className,onClick - Komponenty velkým písmenem —
<MyComponent /> - Výrazy ve složených závorkách —
{expression} - Komentáře ve složených závorkách —
{/* komentář */}
JSX bez Reactu
JSX můžete použít i s jinými knihovnami:
Preact
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}
SolidJS
// vite.config.js
import { defineConfig } from 'vite';
import solidPlugin from 'vite-plugin-solid';
export default defineConfig({
plugins: [solidPlugin()]
});
Vue
Vue 3 podporuje JSX pomocí @vitejs/plugin-vue-jsx.
Tipy
- Extrahujte opakující se JSX do samostatných komponent
- Používejte TypeScript pro lepší typovou kontrolu props
- Vyhněte se inline funkcím v props pro lepší výkon
- Rozbalujte props pro přehlednější kód
- Používejte fragmenty místo zbytečných wrapper divů
Odkazy
Související články
Jak vkládat 3D objekty na web pomocí Three.js
Které formáty použít, jak vytvářet modely pomocí AI a kdy raději použít obrázek nebo video.
JavaScript null a undefined
Rozdíly mezi null a undefined v JavaScriptu, kdy je používat a jak se vyhnout běžným chybám.
Sleep v JavaScriptu
Jak implementovat sleep/delay funkcionalitu v JavaScriptu pomocí Promise a async/await