React 框架集成示例
本教程共 56 篇 · 第 29 篇 · 更新于 2026-08-07 · 约 8 分钟阅读
本节目标:照着做完就能在 Astro 里用上 React 组件,知道 astro.config 与 tsconfig 怎么改,以及多 JSX 框架混用时怎么区分。
安装 @astrojs/react
Astro 提供 astro add 命令,能自动把官方集成装好并改好配置。在项目目录里执行下面任一条,按提示走:
# npm
npx astro add react
# pnpm
pnpm astro add react
# yarn
yarn astro add react
一键安装基本够用。如果它报错或你想完全掌控,就走手动安装。
手动安装
先装包:
npm install @astrojs/react
# 或
pnpm add @astrojs/react
# 或
yarn add @astrojs/react
多数包管理器会顺带把 peer 依赖装好。如果启动 Astro 时看到 Cannot find package 'react' 之类警告,说明你还得手动补上 react 和类型:
npm install react react-dom @types/react @types/react-dom
# 或
pnpm add react react-dom @types/react @types/react-dom
# 或
yarn add react react-dom @types/react @types/react-dom
然后在 astro.config.mjs 里把集成加进 integrations:
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
// ...
integrations: [react()],
});
接着在 tsconfig.json 里加上 JSX 相关设置,告诉 TypeScript 用 React 的方式处理:
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"],
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}
第一个 React 组件
装好之后,按「框架组件通用用法」三步走:import、当标签用、需要交互就加 client:* 指令。详细的水合选项和混用嵌套,前面章节已经讲过,这里不再重复。
多 JSX 框架怎么区分
React、Preact、Solid 都常用 .jsx / .tsx 后缀。当你在一个项目里同时接了其中多个,Astro 需要知道「哪个文件归哪个框架」。
单个 JSX 框架时,什么都不用配。多个时,用 include(必填)和 exclude(可选)指定归属。建议把不同框架的组件分目录放,比如 /components/react/ 和 /components/solid/,配置更省心。
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import svelte from '@astrojs/svelte';
import vue from '@astrojs/vue';
import solid from '@astrojs/solid-js';
export default defineConfig({
// 只用一个 JSX 框架时,不需要 include!
integrations: [
preact({ include: ['**/preact/*'] }),
react({ include: ['**/react/*'] }),
solid({ include: ['**/solid/*'] }),
],
});
children 解析的小坑
从 Astro 组件传给 React 组件的子内容,会被当成「纯字符串」解析,而不是 React 节点。下面这个例子里,ReactComponent 只会收到一个 children:
---
import ReactComponent from './ReactComponent';
---
<ReactComponent>
<div>one</div>
<div>two</div>
</ReactComponent>
如果你用的某个库期望收到多个子节点(比如要把不同元素塞进不同插槽),这就会卡住。可以打开实验性开关 experimentalReactChildren,让 Astro 始终把 children 当 React 虚拟 DOM 节点传过去。代价是有一点运行时开销,但兼容性更好。
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
// ...
integrations: [
react({
experimentalReactChildren: true,
}),
],
});
和 Astro Actions 配合(进阶)
@astrojs/react 提供了两个配合 Astro Actions 的函数:withState() 和 getActionState()。它们和 React 的 useActionState() 钩子一起用,能在表单提交触发 action 时读写客户端状态。
withState() 把 action 和初始状态传给 useActionState()。下面的例子传一个 like action,初始点赞数为 0:
import { actions } from 'astro:actions';
import { withState } from '@astrojs/react/actions';
import { useActionState } from "react";
export function Like({ postId }: { postId: string }) {
const [state, action, pending] = useActionState(
withState(actions.like),
{ data: 0, error: undefined }, // 初始点赞数与错误
);
return (
<form action={action}>
<input type="hidden" name="postId" value={postId} />
<button disabled={pending}>{state.data} ❤️</button>
</form>
);
}
在服务端的 action 处理函数里,可以用 getActionState() 拿到 useActionState() 存的状态。它接受 Astro 的 API 上下文,还能给结果加类型。
import { defineAction, type SafeResult } from 'astro:actions';
import { z } from 'astro/zod';
import { getActionState } from '@astrojs/react/actions';
export const server = {
like: defineAction({
input: z.object({ postId: z.string() }),
handler: async ({ postId }, ctx) => {
const { data: currentLikes = 0, error } = await getActionState<SafeResult<any, number>>(ctx);
if (error) throw error;
return currentLikes + 1;
},
}),
};
withState() 会让 action 的类型匹配 React 的预期,并保留「渐进增强」所需的元信息——就算用户设备关了 JS,它也能正常工作。
关闭流式渲染(实验性)
默认 Astro 会对 React 组件做流式输出。有些库(比如部分 CSS-in-JS 方案)和流式不太合得来,可以打开 experimentalDisableStreaming 关掉它。
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
integrations: [
react({
experimentalDisableStreaming: true,
}),
],
});
写一个 React 计数器组件
装好集成后,在 /src/components 建一个 .jsx 文件,就是一个普通 React 组件:
import { useState } from 'react';
export default function Counter({ initialCount = 0 }) {
const [count, setCount] = useState(initialCount);
return (
<button onClick={() => setCount(count + 1)}>
点了 {count} 次
</button>
);
}
然后在 Astro 页面里 import 它,加 client:load 让它可交互:
---
import Counter from '../components/Counter.jsx';
---
<Counter client:load initialCount={5} />
构建时,Astro 会先在服务端把这个按钮渲染成静态 HTML(显示「点了 5 次」)。页面加载后,框架 JS 下载、水合,按钮才真正能点击。initialCount 这个 prop 会被序列化后传给客户端,所以初始值 5 也能保留。
给 React 组件传 props
从 Astro 传 props 给 React 组件,写法跟普通组件一样。类型得是可序列化的那几种。下面传一个待办数组和一个数字:
---
import TodoList from '../components/TodoList.jsx';
import Counter from '../components/Counter.svelte';
---
<div>
<TodoList initialTodos={["学 Astro", "审 PR"]} />
<Counter startingCount={1} />
</div>
Note字符串、数字、数组、普通对象这类都能传。函数不行——服务端没办法把函数变成客户端能执行的代码。
在 React 里用插槽
React 用名为 children 的 prop 来接子内容。在 Astro 里这么写:
---
import MyReactSidebar from '../components/MyReactSidebar.jsx';
---
<MyReactSidebar>
<p>这是一个带文字和按钮的侧边栏。</p>
</MyReactSidebar>
用具名插槽时,kebab-case 的名字会转成 camelCase 的 prop:
<MySidebar>
<h2 slot="title">菜单</h2>
<p>侧边栏正文</p>
<ul slot="social-links">
<li><a href="https://github.com/withastro">GitHub</a></li>
</ul>
</MySidebar>
在 React 组件里就读 props.title、props.children、props.socialLinks。
React 也能只在客户端渲染
如果某个 React 组件完全依赖浏览器、没有服务端 HTML 也能接受,可以用 client:only="react"。它不会在服务端渲染,只在浏览器生成:
<MyReactModal client:only="react" />
代价是首屏那一瞬会有空白,因为没有服务端 HTML 兜底。非必要别用。
接好后不工作?先查这三处
新手接 React 最容易卡在几种情况,按这个顺序排一遍最快:
- 组件是静态的、点了没反应:十有八九是忘了加
client:*指令。没有指令,Astro 只输出静态 HTML,不会发 JS。补上client:load之类再试。 - 终端报
Cannot find package 'react':peer 依赖没装全。按前面提示把react、react-dom和类型补上。 - 组件内容空白或报错,多 JSX 框架共存时:检查
astro.config.mjs里include目录是否把文件指对了框架。归错框架,编译就会出问题。
还有一类隐蔽问题:从 Astro 传过去的 props 必须可序列化。如果你把「函数」当 prop 传进 React 组件,运行时不会报错在编译期,但水合后那部分逻辑会失效。遇到「交互只做了一半」,先确认没传函数之类的不可序列化数据。
React 在 Astro 里的定位
把 React 接进 Astro,不是把整站改成 React 应用,而是「在静态海洋里放几个 React 岛屿」。其余页面、布局、文章依然是 Astro 原生组件,零框架负担。
这种定位很灵活。你可以只在一个需要复杂状态的角落用 React,比如一个带筛选的数据表格;其他所有地方都不碰 React。这样既享受了 React 的生态,又不牺牲 Astro 的快。
水合过程通俗讲一遍
「水合」听上去玄,其实就三步。第一步,Astro 在服务端把 React 组件渲染成静态 HTML,用户立刻看到内容。第二步,浏览器下载这个组件对应的 JS(含 React 运行时)。第三步,JS 把这段静态 HTML「接上电」,让它变成能点击、能响应事件的活组件。
关键在于:第一步的静态 HTML 已经够看、够读。JS 只是锦上添花,让它能交互。就算 JS 慢一点,用户也不会面对空白页。这就是「默认快」的来源。
别忘了心智负担
多框架共存也有代价。团队成员得知道哪些文件是 React、哪些是 Svelte;构建配置要维护正确。如果你只用一种框架,事情最简单。只有真需要「一个项目里混多种技术」时,才值得上多框架集成。
一点心里话
学完这一章你会发现,接 React 本身不难,难的是「克制」。Astro 的魅力恰恰在于:它不强迫你用框架,也不阻止你用。什么时候该把内容做成静态 HTML、什么时候该升级成 React 岛屿,这个分寸感,才是在 Astro 里用好 React 的关键。
版本小提示
写这篇时官方 React 集成版本是 v6.0.2,配 Astro 7 使用没问题。框架和集成都会迭代,若你将来看到不同版本号,别慌,安装命令和 client:* 用法基本不会变。唯一要留意的,是 astro.config.mjs 里 integrations 那行和 tsconfig 的 JSX 设置,按你当时装的文档照抄就行。
小结
React 集成就是「装包、改 astro.config、改 tsconfig」三件事。多 JSX 框架共存时靠 include 区分目录。下一章我们看 Vue、Svelte、Preact、Solid 各自怎么接。