NatUI
Getting started

First app

Render React state into a real native NatUI window.

Create a React component using NatUI host components:

import { useState } from 'react';
import { Button, Text, TextField, VStack, run } from '@natui/core';

function App() {
  const [name, setName] = useState('');

  return (
    <VStack spacing={12} padding={20} alignment="leading">
      <Text font="title" weight="semibold">Welcome</Text>
      <TextField
        value={name}
        placeholder="Your name"
        onChange={setName}
        frame={{ width: 260 }}
      />
      <Button style="prominent" disabled={!name}>
        {name ? `Continue as ${name}` : 'Continue'}
      </Button>
    </VStack>
  );
}

await run(<App />, {
  title: 'First NatUI app',
  width: 420,
  height: 260,
});

run starts the platform host, validates its handshake, configures the window, and resolves after React's first commit has been flushed to the host transport. Protocol version 1 has no host acknowledgement for a completed native commit. Use dump() in verification code when you need to observe the host tree.

All sizing uses logical points. Native layout decides the final geometry, so expect intentional visual differences between SwiftUI and WinUI 3.

Run it during development

Use an executable src/main.tsx like the example above, then add these package scripts:

{
  "scripts": {
    "dev": "natui dev",
    "start": "tsx src/main.tsx"
  }
}

natui dev reads entry from natui.app.json. A positional entry passed to natui dev overrides the config, and src/main.tsx is the fallback when no config exists. The server watches the application's local source graph and applies React Fast Refresh to the existing native window. Compatible edits preserve hook state. Syntax and evaluation errors keep the last working UI mounted until the next successful edit.

The entry calls the normal run() API once. The repository packager maps that same import to the embedded runtime, so development and packaged builds can share one entry file. See application configuration.

Keep inputs controlled

Interactive values are ordinary React state. Provide both value and onChange when the user should be able to edit a control. NatUI uses sequence acknowledgements to prevent a slower JavaScript round trip from overwriting newer native input.

Continue with controlled state for the complete model.

On this page