Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sanchedev/tiny-engine/llms.txt

Use this file to discover all available pages before exploring further.

tiny-engine ships its own JSX runtime — it has no dependency on React or any other UI framework. When the TypeScript compiler processes your .tsx files it calls tiny-engine’s jsx factory function instead of React.createElement, turning every piece of JSX into a plain { type, props } descriptor that the engine renderer later converts into real Node instances.

Configuring tsconfig.json

Tell TypeScript to use tiny-engine’s runtime by setting jsxImportSource in your project’s tsconfig.json:
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "tiny-engine"
  }
}
With this in place, TypeScript automatically imports jsx, jsxs, and Fragment from tiny-engine/jsx-runtime — you never need to import them manually.

Intrinsic Elements

Intrinsic elements are the lowercase JSX tags that map directly to built-in engine node classes. The full set is typed via JSX.IntrinsicElements, which is derived from PrimaryNode:
JSX TagEngine Node
<transform>Transform
<sprite>Sprite
<animation-player>AnimationPlayer
<collider>Collider
<ray-cast>RayCast
<clickable>Clickable
<timer>Timer
<rectangle>Rectangle
Every intrinsic element also accepts ref (a NodeReference<T> from useRefNode) and any event callback props exposed by that node type.
<sprite
  textureId={PLAYER}
  displaySize={[32, 32]}
  brightness={() => 0.8 + stamina() * 0.2}
/>

Function Components

Function components are plain TypeScript functions that receive props and return a Tiny.Node. This is the standard and recommended way to build reusable game objects in tiny-engine.
import type { Tiny } from 'tiny-engine/jsx'

function Enemy(props: { x: number; y: number }): Tiny.Node {
  return (
    <transform position={[props.x, props.y]}>
      <sprite textureId={ENEMY} />
    </transform>
  )
}

const scene = (
  <transform>
    <Enemy x={100} y={50} />
  </transform>
)

createGame — Bootstrapping from JSX

createGame from tiny-engine/jsx is the idiomatic way to wire up the entire game from a single JSX expression. It calls Game.setup, registers all <Scene> children with the SceneManager, loads the defaultScene, and starts the game loop. It returns a GameControls object so you can interact with the running game programmatically.
import { createGame, Scene } from 'tiny-engine/jsx'

const game = createGame(
  <Game width={320} height={180} defaultScene="main">
    <Scene name="main" component={() => import('./scenes/main.js')} />
    <Scene name="menu" component={() => import('./scenes/menu.js')} />
  </Game>,
  document.querySelector<HTMLElement>('#root')!,
)

// Returned controls
game.play()
game.pause()
await game.changeScene('menu')
const preload = await game.preloadScene('main')
preload() // switch instantly

<List> — Reactive Keyed List Rendering

List is a built-in component (imported from tiny-engine/jsx) for rendering a dynamic, reactively updated list of nodes. It performs keyed reconciliation — nodes are reused, moved, or destroyed based on a key function rather than position, so identity is preserved across updates.
import { List } from 'tiny-engine/jsx'
import { useSignal } from 'tiny-engine/hooks'

function EnemyList() {
  const [enemies, setEnemies] = useSignal([
    { id: 1, x: 0 },
    { id: 2, x: 50 },
  ])

  return (
    <List
      array={enemies}
      itemKey={(enemy) => enemy.id}
      empty={<sprite textureId={EMPTY_INDICATOR} />}
    >
      {(enemy, index) => (
        <transform position={[enemy.x, index * 16]}>
          <sprite textureId={ENEMY} />
        </transform>
      )}
    </List>
  )
}

ListOptions Props

PropTypeRequiredDescription
arrayT[] | SignalGetter<T[]>The data array, static or reactive
itemKey(value, index, arr) => string | symbolUnique key extractor for reconciliation
children(value, index, arr) => Tiny.NodeRender function called for each item
emptyTiny.NodeFallback rendered when the array is empty
List returns a Fragment with a hidden anchor <transform> — no extra wrapper node is added to your scene graph.

Context API

tiny-engine provides createContext and useContext for passing values down the component tree without explicit prop drilling. This is especially useful for sharing game-wide state (score, settings, player reference) across deeply nested components.
import { createContext, useContext } from 'tiny-engine/hooks'

const ScoreCtx = createContext({ score: 0 })

function GameScene() {
  return (
    <ScoreCtx.Provider value={{ score: 42 }}>
      <transform>
        <HUD />
      </transform>
    </ScoreCtx.Provider>
  )
}

function HUD() {
  const { score } = useContext(ScoreCtx)
  return <transform />
}
See the hooks reference for full details.

Fragment Support

Group multiple nodes without introducing a wrapper node using the <>…</> Fragment shorthand. Fragments are flattened into the parent’s children list by the renderer.
function Enemies() {
  return (
    <>
      <transform position={[0, 0]}>
        <sprite textureId={ENEMY_A} />
      </transform>
      <transform position={[64, 0]}>
        <sprite textureId={ENEMY_B} />
      </transform>
    </>
  )
}
Function components in tiny-engine are called exactly once when the scene is built — they are not re-called when state changes. Reactivity works entirely through signal subscriptions wired to individual canvas properties. This means there is no concept of “re-rendering” a component: write your component once, use signals for anything that needs to change at runtime, and the engine handles the rest.

Build docs developers (and LLMs) love