Moderní tvorba webových aplikací

O webu

JSX a TSX

Syntaktické rozšíření JavaScriptu a TypeScriptu pro psaní HTML-like kódu přímo v JS souborech.

15 minut

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 JavaScriptuclass 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 React v 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žaduje import React
  • react-jsx — nový transform (React 17+)
  • react-jsxdev — nový transform s debug info
  • preserve — 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 atributyclassName, 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.

15 minut

Detekce otevření DevTools

Jak zjistit, že se na stránce otevřely vývojářské nástroje.

13 minut

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.

12 minut

Sleep v JavaScriptu

Jak implementovat sleep/delay funkcionalitu v JavaScriptu pomocí Promise a async/await

6 minut

Novinky e-mailem

Když budu mít něco opravdu zajímavého, můžu vám to poslat e-mailem

Přidej se k 500+ čtenářům
Jen kvalitní obsah
Žádný spam

Web jecas.cz píše Bohumil Jahoda, kontakt
Seznam všech článků
2013–2026