Skip to content

JSX/TSX Runtime

This is not a reactive runtime.

It is JSX/TSX factory that produces document.createElement statements.

Configuration

@greycat/web provides a full-fledged JSX runtime by default. In order to use it you need to tell TypeScript how to transpile your JSX/TSX:

json
{
  // ...
  "compilerOptions": {
    // ...
    "jsx": "react-jsx",
    "jsxImportSource": "@greycat/web"
    // ...
  },
  // ...
}
{
  // ...
  "compilerOptions": {
    // ...
    "jsx": "react-jsx",
    "jsxImportSource": "@greycat/web"
    // ...
  },
  // ...
}

The same logic would apply for other transpilers such as Babel.

Example

Then in your .tsx files, you'll get a JSX.Element back from your JSX code:

tsx
export function WrappedTable(table: core.Table) {
  return (
    <article>
      <header>My Table</header>
      <div className="container-fluid">
        <gui-table table={table} />
      </div>
    </article>
  );
}
export function WrappedTable(table: core.Table) {
  return (
    <article>
      <header>My Table</header>
      <div className="container-fluid">
        <gui-table table={table} />
      </div>
    </article>
  );
}
tsx
import { WrappedTable } from './WrappedTable';

const table = await fetchTable();
document.body.appendChild(WrappedTable(table));
import { WrappedTable } from './WrappedTable';

const table = await fetchTable();
document.body.appendChild(WrappedTable(table));

When using JSX/TSX you get exactly the same behavior as if you had written the previous snippet like so:

tsx
export function WrappedTable(table: core.Table) {
  const article = document.createElement('article');

  const header = document.createElement('header');
  header.textContent = 'My Table';

  const div = document.createElement('div');
  div.classList.add('container-fluid');

  const tableEl = document.createElement('gui-table');
  tableEl.table = table;

  article.appendChild(header);
  header.appendChild(div);
  div.appendChild(tableEl);

  return article;
}
export function WrappedTable(table: core.Table) {
  const article = document.createElement('article');

  const header = document.createElement('header');
  header.textContent = 'My Table';

  const div = document.createElement('div');
  div.classList.add('container-fluid');

  const tableEl = document.createElement('gui-table');
  tableEl.table = table;

  article.appendChild(header);
  header.appendChild(div);
  div.appendChild(tableEl);

  return article;
}
tsx
import { WrappedTable } from './WrappedTable';

const table = await fetchTable();
document.body.appendChild(WrappedTable(table));
import { WrappedTable } from './WrappedTable';

const table = await fetchTable();
document.body.appendChild(WrappedTable(table));

Trade-offs

Currently, the trade-offs for the JSX/TSX syntax are:

  • slighty more GC work due to the way TypeScript transpiles properties into inlined objects (note that, by default, React.js for instance, always gets this incurred cost)
  • weak typing integration that forces you to cast to the appropriate root element type
tsx
const div = <div />; // JSX.Element !== HTMLDivElement
const div = <div />; // JSX.Element !== HTMLDivElement
tsx
const div = <div /> as HTMLDivElement;
const div = <div /> as HTMLDivElement;

Working with React.js

When trying to use @greycat/web JSX runtime alongside React.js some TypeScript fine-tuning should be made so that the two different global JSX namespaces do not conflict with each other.

INFO

We provide an already configured template that work with both.

Mainly, the fine-tuning occurs in tsconfig.json where we need to specify the list of types included in the current TypeScript compiler context.

json
{
  // ...
  "compilerOptions": {
    // ...
    "jsx": "react-jsx",
    "jsxImportSource": "@greycat/web",
    "types": ["@greycat/web"],
    // ...
  },
  "exclude": ["your/react/dir"]
  // ...
}
{
  // ...
  "compilerOptions": {
    // ...
    "jsx": "react-jsx",
    "jsxImportSource": "@greycat/web",
    "types": ["@greycat/web"],
    // ...
  },
  "exclude": ["your/react/dir"]
  // ...
}
json
{
  // ...
  "compilerOptions": {
    // ...
    "jsx": "react-jsx",
    "jsxImportSource": "react",
    "types": ["react"],
    // ...
  },
  // ...
}
{
  // ...
  "compilerOptions": {
    // ...
    "jsx": "react-jsx",
    "jsxImportSource": "react",
    "types": ["react"],
    // ...
  },
  // ...
}

This way TypeScript will understand that JSX within your/react/dir is React-related, while the rest will be @greycat/web-related.

INFO

Read Using React.js for more information about using React.js with @greycat/web.