react-email

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Email

React Email

Build and send HTML emails using React components - a modern, component-based approach to email development that works across all major email clients.
使用React组件构建并发送HTML邮件——这是一种基于组件的现代化邮件开发方式,可兼容所有主流邮件客户端。

Installation

安装

New Project

新项目

Scaffold a new React Email project:
sh
npx create-email@latest
cd react-email-starter
npm install
npm run dev
This creates a
react-email-starter
folder with sample templates. Also works with yarn, pnpm, or bun.
快速搭建新的React Email项目:
sh
npx create-email@latest
cd react-email-starter
npm install
npm run dev
此命令会创建一个包含示例模板的
react-email-starter
文件夹,同时支持yarn、pnpm或bun包管理器。

Existing Project

现有项目

Add React Email to an existing codebase:
  1. Install dependencies:
sh
npm install @react-email/components
npm install react-email @react-email/preview-server -D
  1. Create an emails directory:
sh
mkdir emails
  1. Add a preview script to package.json:
json
{
  "scripts": {
    "email:dev": "email dev"
  }
}
  1. Start the preview server:
sh
npm run email:dev
The
--dir
flag specifies where email templates are stored. Adjust the path to match your project structure (e.g.,
src/emails
,
app/emails
).
在现有代码库中添加React Email:
  1. 安装依赖:
sh
npm install @react-email/components
npm install react-email @react-email/preview-server -D
  1. 创建邮件目录:
sh
mkdir emails
  1. 在package.json中添加预览脚本:
json
{
  "scripts": {
    "email:dev": "email dev"
  }
}
  1. 启动预览服务器:
sh
npm run email:dev
--dir
参数用于指定邮件模板的存储位置,可根据项目结构调整路径(例如
src/emails
app/emails
)。

Brand Setup (Required First Step)

品牌配置(第一步必填)

Before creating any email template, gather brand information from the user. This ensures visual consistency across all emails.
在创建任何邮件模板之前,请先收集用户的品牌信息,确保所有邮件的视觉风格一致。

Required Brand Information

必填品牌信息

Ask the user for:
  1. Brand colors
    • Primary color (main brand color for buttons, links, accents)
    • Secondary color (supporting color for backgrounds, borders)
    • Text color (default:
      #1a1a1a
      for light mode)
    • Background color (default:
      #f4f4f5
      for light mode)
  2. Logo
    • Logo image URL (must be absolute URL, PNG or JPEG)
    • Logo dimensions or aspect ratio preference
    • Logo placement preference (centered, left-aligned)
    • Ask user for file and path of the local file if the logo is not a publicly accessible URL.
  3. Typography (optional)
    • Preferred font family (default: system fonts)
    • Custom web font URL if applicable
  4. Style preferences (optional)
    • Modern/minimal vs. classic/traditional
    • Rounded corners vs. sharp edges
    • Spacing density (compact vs. spacious)
    • Never add emojis unless the user explicitly requests them.
请向用户收集以下信息:
  1. 品牌配色
    • 主色调(用于按钮、链接、强调元素的品牌主色)
    • 辅助色(用于背景、边框的支持色)
    • 文本颜色(浅色模式默认值:
      #1a1a1a
    • 背景颜色(浅色模式默认值:
      #f4f4f5
  2. 品牌Logo
    • Logo图片URL(必须为绝对URL,格式为PNG或JPEG)
    • Logo尺寸或宽高比偏好
    • Logo位置偏好(居中、左对齐)
    • 如果Logo没有公开可访问的URL,请询问用户本地文件的路径。
  3. 排版设置(可选)
    • 首选字体族(默认:系统字体)
    • 自定义网页字体URL(如有)
  4. 风格偏好(可选)
    • 现代极简风 vs 经典传统风
    • 圆角 vs 直角
    • 间距密度(紧凑 vs 宽松)
    • 除非用户明确要求,否则请勿添加表情符号。

Brand Configuration Pattern

品牌配置示例

Once gathered, define a reusable Tailwind configuration. Using
satisfies TailwindConfig
provides intellisense support for all configuration options:
tsx
// emails/tailwind.config.ts
import { pixelBasedPreset, type TailwindConfig } from '@react-email/components';

export default {
  presets: [pixelBasedPreset],
  theme: {
    extend: {
      colors: {
        brand: {
          primary: '#007bff',      // User's primary brand color
          secondary: '#6c757d',    // User's secondary color
        },
      },
    },
  },
} satisfies TailwindConfig;

// For non-Tailwind brand assets (optional)
export const brandAssets = {
  logo: {
    src: 'https://example.com/logo.png',  // User's logo URL
    alt: 'Company Name',
    width: 120,
  },
};
收集信息后,定义可复用的Tailwind配置。使用
satisfies TailwindConfig
可获得所有配置选项的智能提示:
tsx
// emails/tailwind.config.ts
import { pixelBasedPreset, type TailwindConfig } from '@react-email/components';

export default {
  presets: [pixelBasedPreset],
  theme: {
    extend: {
      colors: {
        brand: {
          primary: '#007bff',      // 用户提供的品牌主色
          secondary: '#6c757d',    // 用户提供的辅助色
        },
      },
    },
  },
} satisfies TailwindConfig;

// 非Tailwind品牌资源配置(可选)
export const brandAssets = {
  logo: {
    src: 'https://example.com/logo.png',  // 用户提供的Logo URL
    alt: 'Company Name',
    width: 120,
  },
};

Using Tailwind Config in Templates

在模板中使用Tailwind配置

tsx
import * as React from "react";
import tailwindConfig, { brandAssets } from './tailwind.config';
import { Tailwind, Img } from '@react-email/components';

<Tailwind config={tailwindConfig}>
  <Body className="bg-gray-100 font-sans">
    <Container className="max-w-xl mx-auto bg-white p-6">
      <Img
        src={brandAssets.logo.src}
        alt={brandAssets.logo.alt}
        width={brandAssets.logo.width}
        className="mx-auto mb-6"
      />
      <Button className="bg-brand-primary text-white rounded px-5 py-3">
        Call to Action
      </Button>
    </Container>
  </Body>
</Tailwind>
tsx
import * as React from "react";
import tailwindConfig, { brandAssets } from './tailwind.config';
import { Tailwind, Img } from '@react-email/components';

<Tailwind config={tailwindConfig}>
  <Body className="bg-gray-100 font-sans">
    <Container className="max-w-xl mx-auto bg-white p-6">
      <Img
        src={brandAssets.logo.src}
        alt={brandAssets.logo.alt}
        width={brandAssets.logo.width}
        className="mx-auto mb-6"
      />
      <Button className="bg-brand-primary text-white rounded px-5 py-3">
        行动按钮
      </Button>
    </Container>
  </Body>
</Tailwind>

Asset Locations

资源存放位置

Direct users to place brand assets in appropriate locations:
  • Logo and images: Host on a CDN or public URL. For local development, place in
    emails/static/
    .
  • Custom fonts: Use the
    Font
    component with a web font URL (Google Fonts, Adobe Fonts, or self-hosted).
Example prompt for gathering brand info:
"Before I create your email template, I need some brand information to ensure consistency. Could you provide:
  1. Your primary brand color (hex code, e.g., #007bff)
  2. Your logo URL (must be a publicly accessible PNG or JPEG)
  3. Any secondary colors you'd like to use
  4. Style preference (modern/minimal or classic/traditional)"
引导用户将品牌资源存放在合适的位置:
  • Logo和图片:托管在CDN或提供公开URL。本地开发时,可放在
    emails/static/
    目录下。
  • 自定义字体:使用
    Font
    组件搭配网页字体URL(Google Fonts、Adobe Fonts或自托管字体)。
收集品牌信息的示例话术:
"在创建邮件模板之前,我需要一些品牌相关信息以确保风格统一。能否提供以下内容:
  1. 品牌主色调(十六进制代码,例如#007bff)
  2. Logo的公开URL(必须为PNG或JPEG格式)
  3. 如需使用辅助色,请提供对应的颜色值
  4. 风格偏好(现代极简风或经典传统风)"

Basic Email Template

基础邮件模板

Replace the sample email templates. Here is how to create a new email template:
Create an email component with proper structure using the Tailwind component for styling:
tsx
import * as React from "react";
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Heading,
  Text,
  Button,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  verificationUrl: string;
}

export default function WelcomeEmail({ name, verificationUrl }: WelcomeEmailProps) {
  return (
    <Html lang="en">
      <Tailwind
        config={{
          presets: [pixelBasedPreset],
          theme: {
            extend: {
              colors: {
                brand: '#007bff',
              },
            },
          },
        }}
      >
        <Head />
        <Preview>Welcome - Verify your email</Preview>
        <Body className="bg-gray-100 font-sans">
          <Container className="max-w-xl mx-auto p-5">
            <Heading className="text-2xl text-gray-800">
              Welcome!
            </Heading>
            <Text className="text-base text-gray-800">
              Hi {name}, thanks for signing up!
            </Text>
            <Button
              href={verificationUrl}
              className="bg-brand text-white px-5 py-3 rounded block text-center no-underline"
            >
              Verify Email
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

// Preview props for testing
WelcomeEmail.PreviewProps = {
  name: 'John Doe',
  verificationUrl: 'https://example.com/verify/abc123'
} satisfies WelcomeEmailProps;

export { WelcomeEmail };
替换示例邮件模板,以下是创建新邮件模板的方法:
使用Tailwind组件创建结构规范的邮件组件:
tsx
import * as React from "react";
import {
  Html,
  Head,
  Preview,
  Body,
  Container,
  Heading,
  Text,
  Button,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface WelcomeEmailProps {
  name: string;
  verificationUrl: string;
}

export default function WelcomeEmail({ name, verificationUrl }: WelcomeEmailProps) {
  return (
    <Html lang="en">
      <Tailwind
        config={{
          presets: [pixelBasedPreset],
          theme: {
            extend: {
              colors: {
                brand: '#007bff',
              },
            },
          },
        }}
      >
        <Head />
        <Preview>欢迎您 - 请验证您的邮箱</Preview>
        <Body className="bg-gray-100 font-sans">
          <Container className="max-w-xl mx-auto p-5">
            <Heading className="text-2xl text-gray-800">
              欢迎加入!
            </Heading>
            <Text className="text-base text-gray-800">
              您好{name},感谢您的注册!
            </Text>
            <Button
              href={verificationUrl}
              className="bg-brand text-white px-5 py-3 rounded block text-center no-underline"
            >
              验证邮箱
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}

// 用于开发测试的预览属性
WelcomeEmail.PreviewProps = {
  name: 'John Doe',
  verificationUrl: 'https://example.com/verify/abc123'
} satisfies WelcomeEmailProps;

export { WelcomeEmail };

Essential Components

核心组件

See references/COMPONENTS.md for complete component documentation.
Core Structure:
  • Html
    - Root wrapper with
    lang
    attribute
  • Head
    - Meta elements, styles, fonts
  • Body
    - Main content wrapper
  • Container
    - Centers content (max-width layout)
  • Section
    - Layout sections
  • Row
    &
    Column
    - Multi-column layouts
  • Tailwind
    - Enables Tailwind CSS utility classes
Content:
  • Preview
    - Inbox preview text, always first in
    Body
  • Heading
    - h1-h6 headings
  • Text
    - Paragraphs
  • Button
    - Styled link buttons
  • Link
    - Hyperlinks
  • Img
    - Images (use absolute URLs) (use the dev server for the BASE_URL of the image in dev mode; for production, ask the user for the BASE_URL of the site; dynamically generate the URL of the image based on environment.)
  • Hr
    - Horizontal dividers
Specialized:
  • CodeBlock
    - Syntax-highlighted code
  • CodeInline
    - Inline code
  • Markdown
    - Render markdown
  • Font
    - Custom web fonts
完整的组件文档请参考references/COMPONENTS.md
核心结构组件:
  • Html
    - 带
    lang
    属性的根容器
  • Head
    - 元元素、样式、字体配置容器
  • Body
    - 主内容容器
  • Container
    - 居中内容容器(带最大宽度布局)
  • Section
    - 布局区块
  • Row
    &
    Column
    - 多列布局组件
  • Tailwind
    - 启用Tailwind CSS工具类
内容组件:
  • Preview
    - 收件箱预览文本,需放在
    Body
    最顶部
  • Heading
    - h1-h6标题组件
  • Text
    - 段落组件
  • Button
    - 样式化链接按钮
  • Link
    - 超链接组件
  • Img
    - 图片组件(需使用绝对URL;开发模式下使用开发服务器的BASE_URL;生产环境下请询问用户站点的BASE_URL,根据环境动态生成图片URL)
  • Hr
    - 水平分隔线
特殊组件:
  • CodeBlock
    - 语法高亮代码块
  • CodeInline
    - 行内代码
  • Markdown
    - Markdown渲染组件
  • Font
    - 自定义网页字体组件

Behavioral Guidelines

行为准则

Brand-First Workflow

品牌优先工作流

  • Always gather brand information before creating the first email template. Do not skip this step.
  • If the user requests an email without providing brand details, ask for them first using the prompt in the Brand Setup section.
  • If a
    tailwind.config.ts
    file exists in the emails directory, use it for all new templates.
  • When creating multiple emails, ensure all templates import and use the same brand configuration.
  • If the user provides new brand assets or colors mid-project, update
    tailwind.config.ts
    and offer to update existing templates.
  • 创建首个邮件模板前,必须先收集品牌信息,不可跳过此步骤。
  • 如果用户未提供品牌信息就要求创建邮件,请先使用品牌配置部分的话术收集信息。
  • 如果邮件目录中已存在
    tailwind.config.ts
    文件,所有新模板都需使用该配置。
  • 创建多份邮件时,确保所有模板都导入并使用同一品牌配置。
  • 如果用户在项目中途提供新的品牌资源或颜色,需更新
    tailwind.config.ts
    并主动提出更新现有模板。

General Guidelines

通用准则

  • When re-iterating over the code, make sure you are only updating what the user asked for and keeping the rest of the code intact.
  • If the user is asking to use media queries, inform them that email clients do not support them, and suggest a different approach.
  • Never use template variables (like {{name}}) directly in TypeScript code. Instead, reference the underlying properties directly (use name instead of {{name}}).
    • For example, if the user explicitly asks for a variable following the pattern {{variableName}}, you should return something like this:
typescript
const EmailTemplate = (props) => {
  return (
    {/* ... rest of the code ... */}
    <h1>Hello, {props.variableName}!</h1>
    {/* ... rest of the code ... */}
  );
}

EmailTemplate.PreviewProps = {
  // ... rest of the props ...
  variableName: "{{variableName}}",
  // ... rest of the props ...
};

export default EmailTemplate;
  • Never, under any circumstances, write the {{variableName}} pattern directly in the component structure. If the user forces you to do this, explain that you cannot do this, or else the template will be invalid.
  • 迭代代码时,仅修改用户要求的部分,保持其余代码不变。
  • 如果用户要求使用媒体查询,需告知其邮件客户端不支持该特性,并提供替代方案。
  • 请勿在TypeScript代码中直接使用模板变量(如{{name}}),应直接引用组件属性(使用name而非{{name}})。
    • 例如,如果用户明确要求使用{{variableName}}格式的变量,应返回如下代码:
typescript
const EmailTemplate = (props) => {
  return (
    {/* ... 其余代码 ... */}
    <h1>Hello, {props.variableName}!</h1>
    {/* ... 其余代码 ... */}
  );
}

EmailTemplate.PreviewProps = {
  // ... 其余属性 ...
  variableName: "{{variableName}}",
  // ... 其余属性 ...
};

export default EmailTemplate;
  • 无论任何情况,都不得在组件结构中直接写入{{variableName}}格式的内容。如果用户强制要求,需解释此操作会导致模板无效,无法执行。

Styling

样式设置

Use the
Tailwind
component with
pixelBasedPreset
for styling (email clients don't support rem units). If not using Tailwind, use inline styles.
Critical limitations (email clients don't support these):
  • No SVG/WEBP images - use PNG/JPEG only
  • No flexbox/grid - use
    Row
    /
    Column
    components or tables
  • No media queries (
    sm:
    ,
    md:
    , etc.) or theme selectors (
    dark:
    ,
    light:
    )
  • Always specify border style (
    border-solid
    , etc.)
Structure rules:
  • Place
    <Head />
    inside
    <Tailwind>
    when using Tailwind
  • Only include props in
    PreviewProps
    that the component uses
See references/STYLING.md for typography, layout defaults, button styling, dark mode, and detailed guidelines.
使用带
pixelBasedPreset
Tailwind
组件进行样式设置(邮件客户端不支持rem单位)。如果不使用Tailwind,请使用内联样式。
关键限制(邮件客户端不支持以下特性):
  • 不支持SVG/WEBP图片,仅可使用PNG/JPEG格式
  • 不支持flexbox/grid布局,需使用
    Row
    /
    Column
    组件或表格
  • 不支持媒体查询(如
    sm:
    md:
    )或主题选择器(如
    dark:
    light:
  • 必须指定边框样式(如
    border-solid
结构规则:
  • 使用Tailwind时,需将
    <Head />
    放在
    <Tailwind>
    内部
  • PreviewProps
    中仅包含组件实际使用的属性
关于排版、布局默认值、按钮样式、深色模式及详细规范,请参考references/STYLING.md

Rendering

渲染处理

Convert to HTML

转换为HTML

tsx
import { render } from '@react-email/components';
import { WelcomeEmail } from './emails/welcome';

const html = await render(
  <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
);
tsx
import { render } from '@react-email/components';
import { WelcomeEmail } from './emails/welcome';

const html = await render(
  <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
);

Convert to Plain Text

转换为纯文本

tsx
import { render } from '@react-email/components';
import { WelcomeEmail } from './emails/welcome';

const text = await render(<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />, { plainText: true });
tsx
import { render } from '@react-email/components';
import { WelcomeEmail } from './emails/welcome';

const text = await render(<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />, { plainText: true });

Sending

邮件发送

React Email supports sending with any email service provider. If the user wants to know how to send, view the Sending guidelines.
Quick example using the Resend SDK for Node.js:
tsx
import * as React from "react";
import { Resend } from 'resend';
import { WelcomeEmail } from './emails/welcome';

const resend = new Resend(process.env.RESEND_API_KEY);

const { data, error } = await resend.emails.send({
  from: 'Acme <onboarding@resend.dev>',
  to: ['user@example.com'],
  subject: 'Welcome to Acme',
  react: <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
});

if (error) {
  console.error('Failed to send:', error);
}
The Node SDK automatically handles the plain-text rendering and HTML rendering for you.
React Email支持与任意邮件服务提供商集成。如果用户想了解发送方式,请查看发送指南
使用Resend Node.js SDK的快速示例:
tsx
import * as React from "react";
import { Resend } from 'resend';
import { WelcomeEmail } from './emails/welcome';

const resend = new Resend(process.env.RESEND_API_KEY);

const { data, error } = await resend.emails.send({
  from: 'Acme <onboarding@resend.dev>',
  to: ['user@example.com'],
  subject: '欢迎加入Acme',
  react: <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
});

if (error) {
  console.error('发送失败:', error);
}
Node SDK会自动处理纯文本和HTML的渲染工作。

Internationalization

国际化支持

See references/I18N.md for complete i18n documentation.
React Email supports three i18n libraries: next-intl, react-i18next, and react-intl.
完整的国际化文档请参考references/I18N.md
React Email支持三种国际化库:next-intl、react-i18next和react-intl。

Quick Example (next-intl)

快速示例(next-intl)

tsx
import * as React from "react";
import { createTranslator } from 'next-intl';
import {
  Html,
  Body,
  Container,
  Text,
  Button,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface EmailProps {
  name: string;
  locale: string;
}

export default async function WelcomeEmail({ name, locale }: EmailProps) {
  const t = createTranslator({
    messages: await import(\`../messages/\${locale}.json\`),
    namespace: 'welcome-email',
    locale
  });

  return (
    <Html lang={locale}>
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Body className="bg-gray-100 font-sans">
          <Container className="max-w-xl mx-auto p-5">
            <Text className="text-base text-gray-800">{t('greeting')} {name},</Text>
            <Text className="text-base text-gray-800">{t('body')}</Text>
            <Button href="https://example.com" className="bg-blue-600 text-white px-5 py-3 rounded">
              {t('cta')}
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
Message files (`messages/en.json`, `messages/es.json`, etc.):
json
{
  "welcome-email": {
    "greeting": "Hi",
    "body": "Thanks for signing up!",
    "cta": "Get Started"
  }
}
tsx
import * as React from "react";
import { createTranslator } from 'next-intl';
import {
  Html,
  Body,
  Container,
  Text,
  Button,
  Tailwind,
  pixelBasedPreset
} from '@react-email/components';

interface EmailProps {
  name: string;
  locale: string;
}

export default async function WelcomeEmail({ name, locale }: EmailProps) {
  const t = createTranslator({
    messages: await import(\`../messages/\${locale}.json\`),
    namespace: 'welcome-email',
    locale
  });

  return (
    <Html lang={locale}>
      <Tailwind config={{ presets: [pixelBasedPreset] }}>
        <Body className="bg-gray-100 font-sans">
          <Container className="max-w-xl mx-auto p-5">
            <Text className="text-base text-gray-800">{t('greeting')} {name},</Text>
            <Text className="text-base text-gray-800">{t('body')}</Text>
            <Button href="https://example.com" className="bg-blue-600 text-white px-5 py-3 rounded">
              {t('cta')}
            </Button>
          </Container>
        </Body>
      </Tailwind>
    </Html>
  );
}
消息文件(`messages/en.json`、`messages/es.json`等):
json
{
  "welcome-email": {
    "greeting": "Hi",
    "body": "Thanks for signing up!",
    "cta": "Get Started"
  }
}

Email Best Practices

邮件开发最佳实践

  1. Test across email clients - Test in Gmail, Outlook, Apple Mail, Yahoo Mail. Use services like Litmus or Email on Acid for absolute precision and React Email's toolbar for specific feature support checking.
  2. Keep it responsive - Max-width around 600px, test on mobile devices.
  3. Use absolute image URLs - Host on reliable CDN, always include
    alt
    text.
  4. Provide plain text version - Required for accessibility and some email clients. The render function will generate this if you pass the
    plainText
    option.
  5. Keep file size under 102KB - Gmail clips larger emails.
  6. Add proper TypeScript types - Define interfaces for all email props.
  7. Include preview props - Add
    .PreviewProps
    to components for development testing.
  8. Handle errors - Always check for errors when sending emails.
  1. 跨客户端测试 - 在Gmail、Outlook、Apple Mail、Yahoo Mail中测试。如需高精度测试,可使用Litmus或Email on Acid等服务,也可使用React Email的工具栏检查特定功能的支持情况。
  2. 确保响应式 - 最大宽度设置为600px左右,并在移动设备上测试。
  3. 使用绝对图片URL - 托管在可靠的CDN上,务必添加
    alt
    文本。
  4. 提供纯文本版本 - 为了可访问性和兼容部分邮件客户端,这是必需的。如果在render函数中传入
    plainText
    参数,会自动生成纯文本版本。
  5. 文件大小控制在102KB以内 - Gmail会截断更大的邮件。
  6. 添加TypeScript类型 - 为所有邮件属性定义接口。
  7. 包含预览属性 - 为组件添加
    .PreviewProps
    以便开发测试。
  8. 错误处理 - 发送邮件时务必检查错误。

Common Patterns

常见模式

See references/PATTERNS.md for complete examples including:
  • Password reset emails
  • Order confirmations with product lists
  • Notification emails with code blocks
  • Multi-column layouts
  • Email templates with custom fonts
完整示例请参考references/PATTERNS.md,包括:
  • 密码重置邮件
  • 带商品列表的订单确认邮件
  • 带代码块的通知邮件
  • 多列布局
  • 带自定义字体的邮件模板

Additional Resources

额外资源