Maelstrom orchestrates your UI.

An open-source library for AI-orchestrated UI. You author your components as vessels — their states, actions, and size hints — and a model arranges, resizes, and triggers them in real time, around the user's intent.

$ npm install @maelstrom-co/react@alpha
The process

How It Works

Describe the pieces. Maelstrom handles the orchestration.

01

Describe Your Components

Each component gets a Vessel — a declaration that tells the AI everything it needs to know: available states, actions it can trigger, and minimum size requirements.

orders.vessel.ts
import { defineVessel } from "@maelstrom-co/protocol";

export const ordersVessel = defineVessel({
  id: "orders",
  name: "Orders Panel",
  description: "Displays a list of orders",
  defaultVariant: "list",
  variants: {
    list: {
      props: {},
      minSize: { width: 4, height: 3 },
      actions: [
        { name: "selectOrder", transitionsTo: "details" },
      ],
    },
    details: {
      props: {},
      minSize: { width: 6, height: 5 },
    },
  },
});
02

Create Your Instance

Wire vessels to components and create a typed Maelstrom instance. One factory call gives you everything — Provider, Chart, and fully typed hooks.

maelstrom.ts
import { createMaelstrom, vessel } from "@maelstrom-co/react";
import { ordersVessel } from "./orders.vessel";
import OrdersComponent from "./OrdersComponent";

const ordersEntry = vessel(ordersVessel, OrdersComponent);

export const m = createMaelstrom({
  vessels: [ordersEntry],
});
03

User Intent

Voice or text — same API. The user says what they want, and the AI figures out the rest.

Dashboard.tsx
import { m } from "./maelstrom";

function Dashboard() {
  const { processIntent } = m.useMaelstrom();

  // "Show me pending orders"
  // "Open order 1847"
  await processIntent(userMessage);
}
04

Engine Decides

The AI sees your entire UI — every vessel, every state, and all the space it has to work with. It returns a precise plan: which actions to fire and where to place each component.

MaelstromResponse
// Engine returns actions + layout
{
  userMessage: "Opening order #1847",
  executionPlan: [
    { instanceId: "orders-1", action: "selectOrder" }
  ],
  layout: {
    type: "tiling",
    tree: ["orders-1"],
    instances: [{ variant: "details" }],
  },
};
05

UI Responds

Components transition, the layout shifts, everything updates — instantly and type-safe. Your code stays predictable while the AI orchestrates the experience.

App.tsx
import { ok } from "@maelstrom-co/protocol";
import { m } from "./maelstrom";

function OrdersVessel({ instanceId }) {
  m.useActions("orders", instanceId, {
    selectOrder: async ({ orderId }) => {
      loadOrder(orderId);
      return ok(undefined);
    },
  });

  return <OrdersPanel />;
}
orders.vessel.ts
import { defineVessel } from "@maelstrom-co/protocol";

export const ordersVessel = defineVessel({
  id: "orders",
  name: "Orders Panel",
  description: "Displays a list of orders",
  defaultVariant: "list",
  variants: {
    list: {
      props: {},
      minSize: { width: 4, height: 3 },
      actions: [
        { name: "selectOrder", transitionsTo: "details" },
      ],
    },
    details: {
      props: {},
      minSize: { width: 6, height: 5 },
    },
  },
});
01 / 05
maelstrom.ts
import { createMaelstrom, vessel } from "@maelstrom-co/react";
import { ordersVessel } from "./orders.vessel";
import OrdersComponent from "./OrdersComponent";

const ordersEntry = vessel(ordersVessel, OrdersComponent);

export const m = createMaelstrom({
  vessels: [ordersEntry],
});
02 / 05
Dashboard.tsx
import { m } from "./maelstrom";

function Dashboard() {
  const { processIntent } = m.useMaelstrom();

  // "Show me pending orders"
  // "Open order 1847"
  await processIntent(userMessage);
}
03 / 05
MaelstromResponse
// Engine returns actions + layout
{
  userMessage: "Opening order #1847",
  executionPlan: [
    { instanceId: "orders-1", action: "selectOrder" }
  ],
  layout: {
    type: "tiling",
    tree: ["orders-1"],
    instances: [{ variant: "details" }],
  },
};
04 / 05
App.tsx
import { ok } from "@maelstrom-co/protocol";
import { m } from "./maelstrom";

function OrdersVessel({ instanceId }) {
  m.useActions("orders", instanceId, {
    selectOrder: async ({ orderId }) => {
      loadOrder(orderId);
      return ok(undefined);
    },
  });

  return <OrdersPanel />;
}
05 / 05
Experience the engine

See It In Action

This is @maelstrom-co/react running live, not a mockup. The AI responses are pre-recorded so you see a consistent demo.

Want to explore it yourself?

Try the Playground
Open source private beta

Help shape Maelstrom

We are inviting React engineers to test the Maelstrom library in real projects and help us prepare it for public release.

Build something real

Use Maelstrom in your own React project and test it against real product requirements.

01 / 03

Work with the maintainers

Share bugs, rough edges, and missing capabilities directly with the team building the library.

02 / 03

Shape the open source API

Help us make the developer experience solid before the first public release.

03 / 03

Become a beta tester

Join our Discord community for the beta role and instructions to request GitHub access.

Join the beta community on Discord