用 Next.js 与 Remotion 构建可编程视频 SaaS:template-next-pages 模板深度指南
【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion
导读
本模板(仓库内路径 packages/template-next-pages)是 Remotion 官方为「搭建可编程视频应用」准备的 Next.js 起步工程,它选用 Next.jsPages Router(App Router 版本对应 packages/template-next-app),并预置了 @remotion/player(浏览器端实时预览)与 @remotion/lambda(AWS 无服务器渲染)两套能力。读完本文,你将掌握:如何快速生成该模板、理解它的目录架构与渲染链路、读懂config.mjs/deploy.mjs的全部参数、把「前端表单 → Next.js API 路由 → Lambda 渲染 → 进度轮询 → 视频下载」这条 SaaS 视频生成管线完整跑通。
一、模板定位:一个开箱即用的视频生成 SaaS
在 Remotion 官方脚手架(create-video)的模板注册表 packages/create-video/src/templates.ts 中,该模板的登记信息如下:
cliId:next-pages-dir,即用--next-pages-dir参数创建;- 仓库对应目录
templateInMonorepo:template-next-pages(即当前阅读的目录); - 类型
type: 'video',定位是“面向希望构建能生成视频的应用的人的推荐选择”。
也就是说,这个模板不是演示单个视频如何渲染,而是演示一个完整的视频生成产品应该怎样组织代码:浏览器里用@remotion/player实时播放视频并把“输入参数(如标题文本)”作为inputProps传入,点渲染按钮后由 Next.js API 路由调度@remotion/lambda在 AWS 上后台渲染,再轮询渲染进度、最终提供下载链接。
一个值得注意的约束:该模板显式关闭了 Tailwind 集成开关(allowEnableTailwind: false),在 packages/create-video/src/pkg-managers.ts 中对next、next-pages-dir等模板做了相同的排除处理,样式体系走原生 CSS Modules,避免脚手架额外注入样式配置。
二、目录结构速览:各模块各司其职
packages/template-next-pages/ ├── src/ │ ├── pages/ # Next.js 页面与 API 路由(Pages Router) │ │ ├── index.tsx # 首页:Player 预览 + 渲染控制台 │ │ ├── _app.tsx # 应用入口,导入全局样式 │ │ └── api/lambda/ │ │ ├── render.ts # POST:发起 Lambda 渲染 │ │ └── progress.ts # POST:查询渲染进度 │ ├── components/ # 前端 UI:RenderControls、按钮、进度条等 │ ├── helpers/ │ │ ├── api-response.ts # API 统一响应包装与错误处理 │ │ └── use-rendering.ts # 渲染状态机(invoking→rendering→done/error) │ ├── lambda/api.ts # 浏览器端调用 API 路由的封装 │ └── remotion/ # 视频端代码(与 Remotion 的 Composition 注册) │ ├── index.ts # registerRoot │ ├── Root.tsx # Composition 注册处 │ └── MyComp/ # 示例视频 Main / NextLogo / Rings / TextFade ├── types/ │ ├── constants.ts # 合成参数(时长/分辨率/帧率/Props schema) │ └── schema.ts # API 请求/响应 schema ├── config.mjs # Lambda 部署与调用共享配置(唯一事实源) ├── deploy.mjs # 部署函数、存储桶与 bundle 的脚本 ├── remotion.config.ts # Remotion 打包器配置 ├── next.config.js # Next.js 配置 ├── .env.example # AWS 密钥环境变量样例 └── vercel.json # Vercel 构建命令(先部署再构建)目录里最关键的架构决策是types/constants.ts承担“前后端唯一事实源”:它导出 zod 的CompositionProps,前端表单类型、API schema、Player 的inputProps、Lambda 渲染入参全部引用它,避免三处各写一份类型而漂移。
三、快速开始:两种方式拿到工程
模板工程自身位于 monorepo 的 packages/template-next-pages 目录下。要在自己的机器上建独立项目,官方提供两条路径:
- 使用 GitHub 的 “Use this template” 把它克隆到自己的账号下,然后安装依赖:
npm i - 直接用 Remotion 官方脚手架命令创建(这也是模板注册表
cliId: 'next-pages-dir'的用法):npx create-video@latest --next-pages-dir
依赖清单可在 package.json 中查看:运行时核心是remotion、@remotion/player、@remotion/lambda、next、react/react-dom、zod;在 monorepo 内它们以workspace:*形式互相引用,独立脚手架后会自动解析为发布版本。UI 辅助依赖还有@remotion/google-fonts、@remotion/shapes、@remotion/paths。
四、常用命令与脚本映射
模板 README 给出的命令都可以在 package.json 的scripts中找到对应:
| 用途 | 命令 | 脚本/说明 |
|---|---|---|
| 启动 Next.js 开发服务器 | npm run dev | 即next dev,浏览器访问首页预览 Player |
| 打开 Remotion Studio | npm run remotion(或npx remotion studio) | 单独在 Studio 中编辑/预览视频 |
| 本地渲染视频 | npm run render(或npx remotion render) | 无需 AWS,直接在本地用 Chromium 渲染 |
| 升级 Remotion | npx remotion upgrade | 将remotion及@remotion/*升级到最新版 |
| 部署 Lambda | npm run deploy(或node deploy.mjs) | 部署函数、存储桶与 site bundle |
| 构建与静态启动 | npm run build/npm start | next build/next start |
此外 remotion.config.ts 对渲染器做了两项全局设置:Config.setRspack(true)使用 Rspack 作为打包器;Config.setVideoImageFormat("jpeg")让本地渲染的帧图片格式采用 JPEG(体积更小、渲染更快)。注意该文件内注释强调:使用 Node.js API(如 Lambda 的renderMediaOnLambda)时配置文件不生效,参数需直接传给 API。
五、模板背后的渲染链路(页面 → API → Lambda)
模板默认只渲染当前帧浏览器预览与完整云端渲染双模式。核心链路为:
index.tsx (Player 实时预览 + 表单修改 inputProps) │ └─ 点击 "Render video" └─ use-rendering.ts (状态机) └─ src/lambda/api.ts → POST /api/lambda/render └─ pages/api/lambda/render.ts └─ renderMediaOnLambda() [@remotion/lambda] └─ 返回 { renderId, bucketName } └─ 每秒轮询 POST /api/lambda/progress └─ pages/api/lambda/progress.ts └─ getRenderProgress() └─ done → { url, size } → 提供下载按钮5.1 首页:Player 与输入框共用同一份 Props
在 src/pages/index.tsx 中,text状态被useMemo转成inputProps,既传给Player做实时预览,也传给RenderControls发起渲染:
const [text, setText] = useState<string>(defaultMyCompProps.title); const inputProps: z.infer<typeof CompositionProps> = useMemo(() => { return { title: text }; }, [text]); <Player component={Main} inputProps={inputProps} durationInFrames={DURATION_IN_FRAMES} fps={VIDEO_FPS} compositionHeight={VIDEO_HEIGHT} compositionWidth={VIDEO_WIDTH} controls autoPlay loop initiallyMuted />Player直接把 Remotion 组件<Main />当作 React 组件渲染进普通网页,这正是 Remotion 官方推荐的实时预览体验;由于Player 与云端渲染消费的是同一份inputProps,所见即所得。
5.2 渲染状态机:四种状态驱动 UI
src/helpers/use-rendering.ts 用一个可辨识联合类型表达状态:
init:初始态(等待用户输入);invoking:正在调用渲染接口;rendering:拿到renderId/bucketName,已开始轮询进度;done:拿到产物url与体积size;error:任一环节失败,展示error.message。
轮询部分(use-rendering.ts)是 while 循环:每次调用getProgress(),依据返回的判别字段进入error/done/progress分支,progress 分支await wait(1000)后继续查询,即每秒刷新一次进度。
对应地,RenderControls.tsx 根据状态切换 UI:init/invoking/error显示输入框与 “Render video” 按钮;rendering/done显示 ProgressBar 与下载按钮。
5.3 API 路由实现与统一响应包装
模板把 API 的“校验 + 响应包装”抽象成一个高阶函数 executeApi:
export const executeApi = <Res, Req extends ZodType>(schema: Req, handler: (...) => Promise<Res>) => async (req: NextApiRequest, res: NextApiResponse<ApiResponse<Res>>) => { try { const parsed = schema.parse(req.body); // zod 校验请求体 const data = await handler(req, parsed, res); res.status(200).json({ type: "success", data }); } catch (err) { res.status(500).json({ type: "error", message: (err as Error).message }); } };所有接口的响应统一为{ type: "success", data } | { type: "error", message }(见 src/helpers/api-response.ts),浏览器端封装 src/lambda/api.ts 据此做类型收窄:type === "error"时直接 throw。
发起渲染src/pages/api/lambda/render.ts 关键参数:
const result = await renderMediaOnLambda({ codec: "h264", functionName: speculateFunctionName({ diskSizeInMb: DISK, memorySizeInMb: RAM, timeoutInSeconds: TIMEOUT, }), region: REGION as AwsRegion, serveUrl: SITE_NAME, // 指向部署的 site bundle 名 composition: body.id, // 要渲染的 Composition id inputProps: body.inputProps, // 标题文本等 framesPerLambda: 10, downloadBehavior: { type: "download", fileName: "video.mp4" }, });注意functionName不是硬编码,而是通过speculateFunctionName()根据DISK/RAM/TIMEOUT计算——这保证了API 路由找到的正是deploy.mjs部署出来的那个函数,二者永远使用同一份config.mjs,修改配置后只要重新部署即可保持一致。
查询进度src/pages/api/lambda/progress.ts 调用getRenderProgress(),把低层状态翻译成前端友好的三态:
fatalErrorEncountered→{ type: "error", message };done→{ type: "done", url, size };- 否则 →
{ type: "progress", progress: Math.max(0.03, overallProgress) }(下限 0.03,保证进度条不会停留在 0 造成“没反应”的错觉)。
请求与响应的 zod schema 集中在 types/schema.ts:RenderRequest校验{ id, inputProps },ProgressRequest校验{ bucketName, id }。
六、视频端:Composition 注册与示例动画
Remotion 侧入口 src/remotion/index.ts 调用registerRoot(RemotionRoot);Root.tsx 注册了两个 Composition:
MyComp:1280×720、30fps、200 帧,即主示例视频;NextLogo:140×140、300 帧,Logo 独立动画。
参数常量集中在 types/constants.ts:COMP_NAME = "MyComp"、DURATION_IN_FRAMES = 200、VIDEO_WIDTH = 1280、VIDEO_HEIGHT = 720、VIDEO_FPS = 30,Props schema 为{ title: string },默认标题 “Next.js and Remotion”。
示例视频本体 src/remotion/MyComp/Main.tsx 是一段很典型的 Remotion 动画代码:用spring驱动 Logo 退场(damping: 200,delay从2 * fps帧开始),用两个Sequence控制“Logo/光环”与“标题淡入”的时间轴;字体来自@remotion/google-fonts/Inter的loadFont()(400/700 字重)。标题文字的“擦除式”进场由 TextFade.tsx 实现——它把spring进度经interpolate映射为 mask 渐变的左右端点,产生从左到右渐显的动效。
这一整块src/remotion正是「改变视频模板后需要重新部署 bundle」的那部分代码,与后面的部署流程直接相关。
七、config.mjs:Lambda 渲染的共享配置
config.mjs 是整个云端渲染的“唯一事实源”,导出 5 个值:
| 变量 | 默认值 | 含义 |
|---|---|---|
REGION | "us-east-1" | AWS 区域,编辑器可提示全部合法值(AwsRegion类型) |
SITE_NAME | "my-next-app" | site bundle 在存储桶中的名称,也是serveUrl |
RAM | 3009 | Lambda 内存(MB) |
DISK | 10240 | Lambda/tmp磁盘(MB,即 10 GB) |
TIMEOUT | 240 | 单次函数调用超时(秒) |
RAM、DISK、TIMEOUT三个值会在deploy.mjs的deployFunction()和 API 路由的speculateFunctionName()两处同时使用:前者按此规格创建函数,后者据此推导函数名。因此若直接修改config.mjs,必须重新运行部署脚本,保证实际函数规格与函数名同步更新。
八、deploy.mjs:一键完成“函数 + 存储桶 + Bundle”部署
deploy.mjs 依次执行三件事:
deployFunction({ createCloudWatchLogGroup: true, memorySizeInMb: RAM, region: REGION, timeoutInSeconds: TIMEOUT, diskSizeInMb: DISK })—— 创建(或复用)渲染函数,输出函数名与(created)/(already existed);getOrCreateBucket({ region })—— 创建(或复用)S3 存储桶,Lambda 渲染的中间产物与最终视频都存放在此;deploySite({ bucketName, entryPoint: path.join(process.cwd(), "src", "remotion", "index.ts"), siteName: SITE_NAME, region: REGION })—— 将src/remotion/index.ts作为入口打包并上传 bundle。
脚本开头会做环境变量守卫(deploy.mjs):若同时缺失AWS_ACCESS_KEY_ID/REMOTION_AWS_ACCESS_KEY_ID(及对应的 secret 变量),则提示先去完成 Lambda 设置并以退出码 0 结束,而不是在未配置密钥时报晦涩的 AWS 错误。运行:node deploy.mjs或npm run deploy。
README 特别提醒:以下三种变更之后都应重跑该脚本:
- 修改了视频模板(
src/remotion下的 Composition 代码); - 修改了
config.mjs(区域、内存、超时、site 名等); - 将 Remotion 升级到了新版本。
这也与脚本结尾的提示文案一一对应(deploy.mjs)。
九、启用 AWS Lambda 云端渲染(三步走)
- 复制环境变量样例并填充密钥:将 .env.example 复制为
.env:REMOTION_AWS_ACCESS_KEY_ID="" REMOTION_AWS_SECRET_ACCESS_KEY=""在 AWS 上完成 IAM/凭据开通后填入这两个变量。
.env*.local已被 .gitignore 忽略,密钥不会进入版本库。deploy 脚本与 API 路由(render.ts)同时兼容AWS_*与REMOTION_AWS_*两种变量名,并给出缺失时的中文指引式报错。 - 按需编辑
config.mjs的区域、内存与超时等参数。 - 运行
node deploy.mjs,脚本会创建渲染函数与存储桶并上传 bundle,直到输出 “You now have everything you need to render videos!”。
如果你计划把前端部署到 Vercel,模板自带的 vercel.json 已把构建命令设为node deploy.mjs && next build——即在构建前端之前先确保云端函数与 bundle 就绪,避免线上访问渲染接口时发现函数尚未创建。
十、本地渲染与 Studio:不需要 AWS 也能出片
云端渲染适用于生产环境;日常开发迭代可以用两种本地方式:
npx remotion studio打开 Remotion Studio,逐帧编辑动画、调试合成参数(时长/尺寸/帧率在 types/constants.ts 修改);npx remotion render在本地用无头 Chromium 直接输出 MP4,适合 CI 与无 AWS 环境下的快速验证。
前端、Studio、本地渲染、Lambda 渲染复用同一套 Composition 代码与常量定义,这也是本模板前后端一致的架构基础。
十一、小结:模板给你划好的三条边界
读完本模板能清晰看到 Remotion 官方对“视频生成 SaaS”推荐的工程边界:
- 视频内容与产品解耦:
src/remotion只负责“画面”,通过 zod Props 与外部交互,替换业务视频只需改这个目录并重跑node deploy.mjs; - 前端与渲染解耦:Next.js API 路由薄薄一层,负责鉴权/参数校验/调度,云端渲染细节收敛在
@remotion/lambda; - 配置集中:
config.mjs+.env分别管理非敏感参数与密钥,deploy.mjs与 API 路由永远基于同一份配置推导函数名与参数,避免“部署 A 规格、调用 B 规格”的错位。
License 方面,Remotion 对部分实体企业使用另有要求,详见仓库根目录 LICENSE.md。
【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考