react-native

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

assistant-ui React Native

assistant-ui React Native

Always consult assistant-ui.com/llms.txt for the latest API.
@assistant-ui/react-native
supplies runtime-connected, unstyled React Native primitives. Use them with
View
,
Text
,
Pressable
,
TextInput
,
FlatList
, and native styles to build the chat surface.
@assistant-ui/ai-sdk
supplies the AI SDK v7 runtime and transport. The model route runs in a separate backend project, never inside the Expo bundle.
请始终查阅assistant-ui.com/llms.txt获取最新API。
@assistant-ui/react-native
提供与运行时连接的无样式React Native原语。可结合
View
、
Text
、
Pressable
、
TextInput
、
FlatList
和原生样式构建聊天界面。
@assistant-ui/ai-sdk
提供AI SDK v7运行时和传输层。模型路由运行在独立的后端项目中,绝不会包含在Expo包内。

Contents

目录

References

参考资料

  • ./references/primitives.md -- native primitive namespaces and their parts
  • ./references/hooks.md -- state, runtime, tool, and scoped-provider hooks
  • ./references/adapters.md -- local persistence, attachments, and remote thread lists
  • ./references/custom-backend.md -- a streaming model adapter and a backend-owned thread list
  • ./references/migration.md -- moving a web runtime to a native UI layer
  • ./references/primitives.md -- 原生原语命名空间及其组成部分
  • ./references/hooks.md -- 状态、运行时、工具和作用域提供者钩子
  • ./references/adapters.md -- 本地持久化、附件和远程线程列表
  • ./references/custom-backend.md -- 流式模型适配器和后端托管的线程列表
  • ./references/migration.md -- 将网页运行时迁移到原生UI层

Quick start

快速开始

Start from the maintained Expo example:
sh
npx assistant-ui@latest create --example with-expo my-app
cd my-app
Set an endpoint that the app can reach. It must be an absolute URL. A physical device cannot resolve its own localhost to your development server.
dotenv
EXPO_PUBLIC_CHAT_ENDPOINT_URL="https://api.example.com/api/chat"
Start Expo:
sh
npx expo start
从官方维护的Expo示例项目开始:
sh
npx assistant-ui@latest create --example with-expo my-app
cd my-app
设置应用可访问的端点,必须为绝对URL。物理设备无法将自身的localhost解析到你的开发服务器。
dotenv
EXPO_PUBLIC_CHAT_ENDPOINT_URL="https://api.example.com/api/chat"
启动Expo:
sh
npx expo start

Manual setup

手动设置

Install the native runtime and its AI SDK v7 peer packages in an existing Expo app:
sh
npx expo install @assistant-ui/react-native @assistant-ui/ai-sdk ai@^7 @ai-sdk/react@^4
Host the model route separately. The native app posts UI messages to that route through
AssistantChatTransport
; the route converts them asynchronously for AI SDK v7 and returns a UI message stream.
ts
import { openai } from "@ai-sdk/openai";
import { convertToModelMessages, streamText } from "ai";

export async function POST(request: Request) {
  const { messages } = await request.json();
  const result = streamText({
    model: openai("gpt-5.6-luna"),
    messages: await convertToModelMessages(messages),
  });

  return result.toUIMessageStreamResponse();
}
Create the runtime in a hook. Keep the endpoint in the public Expo environment so the compiled app can reach it.
tsx
import {
  AssistantChatTransport,
  useChatRuntime,
} from "@assistant-ui/ai-sdk";

const chatEndpoint = process.env.EXPO_PUBLIC_CHAT_ENDPOINT_URL;

export function useAppRuntime() {
  if (!chatEndpoint) {
    throw new Error("EXPO_PUBLIC_CHAT_ENDPOINT_URL is required");
  }

  return useChatRuntime({
    transport: new AssistantChatTransport({ api: chatEndpoint }),
  });
}
AssistantChatTransport
forwards frontend tool schemas and system messages. When the backend enables frontend tools, consume the request tools with
frontendTools
from
@assistant-ui/ai-sdk
; see tools for the shared backend contract.
在现有Expo应用中安装原生运行时及其AI SDK v7依赖包:
sh
npx expo install @assistant-ui/react-native @assistant-ui/ai-sdk ai@^7 @ai-sdk/react@^4
单独托管模型路由。原生应用通过
AssistantChatTransport
向该路由发送UI消息;路由会将消息异步转换为AI SDK v7兼容格式,并返回UI消息流。
ts
import { openai } from "@ai-sdk/openai";
import { convertToModelMessages, streamText } from "ai";

export async function POST(request: Request) {
  const { messages } = await request.json();
  const result = streamText({
    model: openai("gpt-5.6-luna"),
    messages: await convertToModelMessages(messages),
  });

  return result.toUIMessageStreamResponse();
}
在钩子中创建运行时。将端点保存在Expo公共环境变量中,以便编译后的应用可以访问它。
tsx
import {
  AssistantChatTransport,
  useChatRuntime,
} from "@assistant-ui/ai-sdk";

const chatEndpoint = process.env.EXPO_PUBLIC_CHAT_ENDPOINT_URL;

export function useAppRuntime() {
  if (!chatEndpoint) {
    throw new Error("EXPO_PUBLIC_CHAT_ENDPOINT_URL is required");
  }

  return useChatRuntime({
    transport: new AssistantChatTransport({ api: chatEndpoint }),
  });
}
AssistantChatTransport
会转发前端工具模式和系统消息。当后端启用前端工具时,使用
@assistant-ui/ai-sdk
中的
frontendTools
处理请求工具;有关共享后端协议,请参考tools。

Native chat composition

原生聊天组合

Put the runtime under the native
AssistantRuntimeProvider
, then compose the thread and composer from primitives.
ThreadPrimitive.MessagesFlatList
is the current list primitive and scopes each row to the corresponding message.
tsx
import {
  AssistantRuntimeProvider,
  AuiIf,
  ComposerPrimitive,
  MessagePrimitive,
  ThreadPrimitive,
  useAuiState,
} from "@assistant-ui/react-native";
import { Text, View } from "react-native";
import { useAppRuntime } from "./use-app-runtime";

function MessageRow() {
  const role = useAuiState((s) => s.message.role);

  return (
    <View
      style={{
        alignSelf: role === "user" ? "flex-end" : "flex-start",
        backgroundColor: role === "user" ? "#007aff" : "#f0f0f0",
        borderRadius: 16,
        margin: 8,
        padding: 12,
      }}
    >
      <MessagePrimitive.Content />
    </View>
  );
}

function Composer() {
  return (
    <ComposerPrimitive.Root style={{ flexDirection: "row", gap: 8, padding: 12 }}>
      <ComposerPrimitive.Input
        multiline
        placeholder="Message..."
        style={{ borderWidth: 1, borderRadius: 20, flex: 1, padding: 10 }}
      />
      <ComposerPrimitive.Send>
        <Text>Send</Text>
      </ComposerPrimitive.Send>
    </ComposerPrimitive.Root>
  );
}

function ChatScreen() {
  return (
    <ThreadPrimitive.Root style={{ flex: 1 }}>
      <AuiIf condition={(s) => s.thread.isEmpty}>
        <Text style={{ padding: 16 }}>Send a message to begin.</Text>
      </AuiIf>
      <ThreadPrimitive.MessagesFlatList autoScroll>
        {() => <MessageRow />}
      </ThreadPrimitive.MessagesFlatList>
      <Composer />
    </ThreadPrimitive.Root>
  );
}

export default function App() {
  const runtime = useAppRuntime();

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      <ChatScreen />
    </AssistantRuntimeProvider>
  );
}
MessagePrimitive.Content
defaults text parts to native
Text
, not Markdown. Pass
renderText
or use
MessagePrimitive.Parts
with a React Native Markdown renderer when the model returns Markdown. For native keyboard handling, place the thread in a
KeyboardAvoidingView
and tune it for the platform.
Use
useAuiState
inside primitive scopes for reactive values. Use
useAui()
with no arguments for imperative actions such as
aui.composer.send()
and
aui.thread.cancelRun()
. Keep selectors to primitives or stable references instead of constructing an object or array in the selector.
将运行时置于原生
AssistantRuntimeProvider
之下,然后使用原语组合线程和编辑器。
ThreadPrimitive.MessagesFlatList
是当前的列表原语,会将每一行与对应的消息绑定作用域。
tsx
import {
  AssistantRuntimeProvider,
  AuiIf,
  ComposerPrimitive,
  MessagePrimitive,
  ThreadPrimitive,
  useAuiState,
} from "@assistant-ui/react-native";
import { Text, View } from "react-native";
import { useAppRuntime } from "./use-app-runtime";

function MessageRow() {
  const role = useAuiState((s) => s.message.role);

  return (
    <View
      style={{
        alignSelf: role === "user" ? "flex-end" : "flex-start",
        backgroundColor: role === "user" ? "#007aff" : "#f0f0f0",
        borderRadius: 16,
        margin: 8,
        padding: 12,
      }}
    >
      <MessagePrimitive.Content />
    </View>
  );
}

function Composer() {
  return (
    <ComposerPrimitive.Root style={{ flexDirection: "row", gap: 8, padding: 12 }}>
      <ComposerPrimitive.Input
        multiline
        placeholder="Message..."
        style={{ borderWidth: 1, borderRadius: 20, flex: 1, padding: 10 }}
      />
      <ComposerPrimitive.Send>
        <Text>Send</Text>
      </ComposerPrimitive.Send>
    </ComposerPrimitive.Root>
  );
}

function ChatScreen() {
  return (
    <ThreadPrimitive.Root style={{ flex: 1 }}>
      <AuiIf condition={(s) => s.thread.isEmpty}>
        <Text style={{ padding: 16 }}>Send a message to begin.</Text>
      </AuiIf>
      <ThreadPrimitive.MessagesFlatList autoScroll>
        {() => <MessageRow />}
      </ThreadPrimitive.MessagesFlatList>
      <Composer />
    </ThreadPrimitive.Root>
  );
}

export default function App() {
  const runtime = useAppRuntime();

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      <ChatScreen />
    </AssistantRuntimeProvider>
  );
}
MessagePrimitive.Content
默认将文本部分渲染为原生
Text
,而非Markdown。当模型返回Markdown时,可传入
renderText
或结合
MessagePrimitive.Parts
使用React Native Markdown渲染器。对于原生键盘处理,可将线程置于
KeyboardAvoidingView
中,并根据平台调整配置。
在原语作用域内使用
useAuiState
获取响应式值。使用无参数的
useAui()
执行命令式操作,例如
aui.composer.send()
和
aui.thread.cancelRun()
。选择器应指向原语或稳定引用,而非在选择器中构造对象或数组。

Generative toolkits

生成式工具包

Metro
must compile files that start with
"use generative"
. Install
@assistant-ui/metro
and wrap the default config. Expo gets
getDefaultConfig
from
expo/metro-config
; a bare React Native app gets it from
@react-native/metro-config
.
js
const { getDefaultConfig } = require("expo/metro-config");
const { withAui } = require("@assistant-ui/metro");

module.exports = withAui(getDefaultConfig(__dirname));
For a backendless toolkit, pass the
aui
option through the wrapper so frontend and human tool schemas remain uploadable:
js
module.exports = withAui({
  ...getDefaultConfig(__dirname),
  aui: { backendless: true },
});
Write the toolkit against
@assistant-ui/react-native
, including native
View
and
Text
renderers. The
"use generative"
directive makes the compiler infer tool kind from
execute
. The tools skill covers backend, frontend, human, provider, external, and stub tool semantics.
Metro
必须编译以
"use generative"
开头的文件。安装
@assistant-ui/metro
并包装默认配置。Expo从
expo/metro-config
获取
getDefaultConfig
;原生React Native应用从
@react-native/metro-config
获取。
js
const { getDefaultConfig } = require("expo/metro-config");
const { withAui } = require("@assistant-ui/metro");

module.exports = withAui(getDefaultConfig(__dirname));
对于无后端工具包,通过包装器传递
aui
选项,确保前端和人工工具模式保持可上传状态:
js
module.exports = withAui({
  ...getDefaultConfig(__dirname),
  aui: { backendless: true },
});
针对
@assistant-ui/react-native
编写工具包,包括原生
View
和
Text
渲染器。
"use generative"
指令使编译器从
execute
推断工具类型。tools技能涵盖后端、前端、人工、提供者、外部和存根工具的语义。

Common Gotchas

常见问题

The mobile app posts to /api/chat and never reaches the server
  • Native apps have no browser origin for relative requests. Set
    EXPO_PUBLIC_CHAT_ENDPOINT_URL
    to the complete route URL.
  • Use a host reachable from the simulator or physical device. Device localhost is the device itself.
The provider is mounted but primitives throw or show no state
  • Create the runtime with
    useChatRuntime
    or another supported runtime hook, then pass it as
    runtime={runtime}
    to
    AssistantRuntimeProvider
    .
  • Render primitives below that provider. Message, part, attachment, queue, suggestion, and thread-list-item primitives also need their corresponding parent render scope.
A web Thread or an Elements import does not render in the Expo app
  • The shadcn Elements catalog is DOM and Tailwind based. Build the native UI with
    @assistant-ui/react-native
    primitives and React Native styles.
The message list does not stay at the bottom
  • Use
    ThreadPrimitive.MessagesFlatList
    with
    autoScroll
    for new screens.
    ThreadPrimitive.Messages
    is retained for compatibility and defaults its auto-scroll options to false.
Toolkits compile as ordinary modules or tool schemas never reach the model
  • Add
    withAui
    to
    Metro
    before using
    "use generative"
    .
  • Import the identical generative module in the server build. If there is no server build, set
    aui: { backendless: true }
    .
Markdown appears as literal asterisks and fences
  • MessagePrimitive.Content
    uses native
    Text
    by default. Supply a native Markdown renderer through
    renderText
    .
移动应用向/api/chat发送请求但无法连接到服务器
  • 原生应用没有浏览器源来处理相对请求。请将
    EXPO_PUBLIC_CHAT_ENDPOINT_URL
    设置为完整的路由URL。
  • 使用模拟器或物理设备可访问的主机。设备的localhost指向设备自身。
提供者已挂载但原语抛出错误或无状态显示
  • 使用
    useChatRuntime
    或其他受支持的运行时钩子创建运行时,然后将其作为
    runtime={runtime}
    传递给
    AssistantRuntimeProvider
    。
  • 在该提供者下方渲染原语。消息、片段、附件、队列、建议和线程列表项原语还需要对应的父渲染作用域。
网页Thread或Elements导入无法在Expo应用中渲染
  • shadcn Elements目录基于DOM和Tailwind构建。请使用
    @assistant-ui/react-native
    原语和React Native样式构建原生UI。
消息列表无法保持在底部
  • 对于新界面,使用带有
    autoScroll
    的
    ThreadPrimitive.MessagesFlatList
    。
    ThreadPrimitive.Messages
    仅为兼容保留,其自动滚动选项默认设为false。
工具包编译为普通模块或工具模式从未传递到模型
  • 在使用
    "use generative"
    之前,将
    withAui
    添加到
    Metro
    配置中。
  • 在服务器构建中导入完全相同的生成式模块。如果没有服务器构建,请设置
    aui: { backendless: true }
    。
Markdown显示为字面星号和围栏
  • MessagePrimitive.Content
    默认使用原生
    Text
    。请通过
    renderText
    提供原生Markdown渲染器。

Related Skills

相关技能

  • setup --
    create
    ,
    initialize
    , and configure assistant-ui web projects
  • primitives -- DOM primitives for web applications, not React Native UI
  • runtime -- runtime behavior and backend transport concepts shared with native
  • tools -- generative toolkits, their backend route, and custom tool UI
  • setup --
    create
    、
    initialize
    和配置assistant-ui网页项目
  • primitives -- 用于网页应用的DOM原语,不适用于React Native UI
  • runtime -- 与原生共享的运行时行为和后端传输概念
  • tools -- 生成式工具包、其后端路由和自定义工具UI