# Migration from Web
URL: /docs/ink/migration

Migrate an existing @assistant-ui/react app to the terminal with React Ink.

If you already have an assistant-ui web app, most of your code transfers directly. The runtime core is shared between `@assistant-ui/react` and `@assistant-ui/react-ink` via `@assistant-ui/core` — only the UI layer changes.

What stays the same \[#what-stays-the-same]

* **Runtime setup** — `useLocalRuntime`, `ChatModelAdapter`, and all runtime options work identically.
* **AI SDK integration** — `@assistant-ui/react-ai-sdk` works with React Ink. Your runtime setup transfers directly.
* **Tool definitions** — `useAssistantTool`, `makeAssistantTool`, and tool UI renderers use the same API.
* **State hooks** — `useAuiState`, `useAui`, and selector patterns are the same.
* **Backend code** — Your API routes, streaming endpoints, and server-side logic need zero changes.

What changes \[#what-changes]

| Web (`@assistant-ui/react`)               | Terminal (`@assistant-ui/react-ink`)                        |
| ----------------------------------------- | ----------------------------------------------------------- |
| `AssistantRuntimeProvider`                | `AssistantProvider` (same purpose)                          |
| DOM primitives (`div`, `button`, `input`) | Ink primitives (`Box`, `Text`, `TextInput`)                 |
| CSS / Tailwind styling                    | Ink's flexbox + ANSI colors                                 |
| `@assistant-ui/ui` (shadcn components)    | Build your own terminal UI with primitives                  |
| `@assistant-ui/react-markdown`            | `@assistant-ui/react-ink-markdown` (ANSI-rendered markdown) |

Step-by-step \[#step-by-step]

<Steps>
  <Step>
    Install the React Ink package \[#install-the-react-ink-package]

    ```sh
    npm install @assistant-ui/react-ink ink react
    ```
  </Step>

  <Step>
    Keep your runtime hook \[#keep-your-runtime-hook]

    Your `useLocalRuntime` setup works as-is — no changes needed:

    ```tsx title="hooks/use-app-runtime.ts"
    // This file is identical to your web version
    import { useLocalRuntime, type ChatModelAdapter } from "@assistant-ui/react-ink";

    export function useAppRuntime(adapter: ChatModelAdapter) {
      return useLocalRuntime(adapter);
    }
    ```

    <Callout type="tip">
      If you use a monorepo, you can share the adapter between web and terminal projects directly.
    </Callout>
  </Step>

  <Step>
    Replace the provider \[#replace-the-provider]

    ```tsx
    // Before (web)
    // import { AssistantRuntimeProvider } from "@assistant-ui/react";

    // After (Terminal) — same concept, different package
    import { AssistantProvider } from "@assistant-ui/react-ink";

    export function App() {
      const runtime = useAppRuntime(adapter);
      return (
        <AssistantProvider runtime={runtime}>
          {/* your terminal UI */}
        </AssistantProvider>
      );
    }
    ```
  </Step>

  <Step>
    Rebuild the UI layer \[#rebuild-the-ui-layer]

    This is the main migration effort. Web components don't render in the terminal. Replace them with Ink primitives:

    ```tsx
    // Web — DOM-based
    // import { Thread } from "@/components/assistant-ui/thread";

    // Terminal — use Ink primitives
    import {
      ThreadRoot,
      ThreadMessages,
      ComposerInput,
    } from "@assistant-ui/react-ink";
    import { Box, Text } from "ink";

    function ChatScreen() {
      return (
        <ThreadRoot>
          <ThreadMessages
            renderMessage={({ message }) => (
              <Box marginBottom={1}>
                <Text>
                  {message.content.filter((p) => p.type === "text").map((p) => p.text).join("\n")}
                </Text>
              </Box>
            )}
          />
          <Box borderStyle="round" borderColor="gray" paddingX={1}>
            <ComposerInput submitOnEnter placeholder="Message..." autoFocus />
          </Box>
        </ThreadRoot>
      );
    }
    ```

    See the [Primitives reference](/docs/ink/primitives) for the full list of available components.
  </Step>
</Steps>

Monorepo code sharing \[#monorepo-code-sharing]

In a monorepo, you can share everything except the UI layer:

```
packages/
  shared/
    hooks/          ← useAppRuntime with ChatModelAdapter (shared)
    tools/          ← tool definitions (shared)
  web/
    components/     ← DOM-based UI
  terminal/
    components/     ← Ink-based terminal UI
```

The runtime hook, tool definitions, and backend code are platform-agnostic. Only the UI components need separate implementations for web and terminal.
