DevToolBox免费
博客

SVG 转 React 组件:完整转换指南

8 分钟阅读作者 DevToolBox

SVG 转换为 React 组件是现代前端开发的关键技能。SVG 提供分辨率无关的图形,在每个屏幕上都清晰锐利,而 React 提供组件模型使其可复用和交互。本指南涵盖手动转换技术、自动化工具、无障碍最佳实践和性能优化策略。

使用我们的免费在线工具即时将 SVG 转换为 JSX。

为什么要将 SVG 转换为 React 组件?

将 SVG 作为 React 组件使用比传统的 <img> 标签或 CSS 背景图有诸多优势:通过 props 和 CSS-in-JS 完全控制样式、动画单个路径、动态主题支持(亮/暗模式)以及 tree-shaking。

React 将 JSX 元素视为一等公民,因此 SVG 组件可以接受 props、响应事件、参与组件生命周期。这使得基于 SVG 的图标和图表比静态图片文件强大得多。

主要挑战是原始 SVG 使用 HTML 属性(如 classfill-rule)必须转换为 JSX 等价属性classNamefillRule)。

方法一:手动 SVG 转 React 转换

对于单个图标或简单 SVG,手动转换很简单:

  1. 从设计工具复制原始 SVG 标记。
  2. class 替换为 className
  3. 将连字符属性转为驼峰命名。
  4. xlink:href 转为 xlinkHref
  5. 自闭合无子元素的标签。
  6. 包装在 React 组件函数中并导出。

以下是一个简单对勾图标的转换前后示例:

Raw SVG (before conversion)

<!-- Raw SVG from a design tool -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
     viewBox="0 0 24 24" class="icon-check"
     fill="none" stroke="currentColor" stroke-width="2"
     stroke-linecap="round" stroke-linejoin="round">
  <polyline points="20 6 9 17 4 12"></polyline>
</svg>

React Component (after conversion)

import React from 'react';

interface CheckIconProps {
  size?: number;
  color?: string;
  className?: string;
}

const CheckIcon: React.FC<CheckIconProps> = ({
  size = 24,
  color = 'currentColor',
  className = '',
}) => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width={size}
    height={size}
    viewBox="0 0 24 24"
    className={`icon-check ${className}`}
    fill="none"
    stroke={color}
    strokeWidth={2}           // was stroke-width
    strokeLinecap="round"     // was stroke-linecap
    strokeLinejoin="round"    // was stroke-linejoin
    role="img"
    aria-label="Checkmark"
  >
    <polyline points="20 6 9 17 4 12" />
  </svg>
);

export default CheckIcon;

Common SVG Attribute Conversions

HTML Attribute        JSX Equivalent
─────────────────────────────────────────
class                 className
fill-rule             fillRule
clip-path             clipPath
clip-rule             clipRule
fill-opacity          fillOpacity
stroke-width          strokeWidth
stroke-linecap        strokeLinecap
stroke-linejoin       strokeLinejoin
stroke-dasharray      strokeDasharray
stroke-dashoffset     strokeDashoffset
stroke-miterlimit     strokeMiterlimit
stroke-opacity        strokeOpacity
font-size             fontSize
font-family           fontFamily
text-anchor           textAnchor
text-decoration       textDecoration
dominant-baseline     dominantBaseline
alignment-baseline    alignmentBaseline
xlink:href            xlinkHref (deprecated)
xml:space             xmlSpace

方法二:使用 SVGR 自动转换

SVGR 是将 SVG 文件转换为 React 组件的行业标准工具,支持 CRA、Next.js 和 Vite。

可通过 CLI、webpack/Vite 插件或 Node.js API 使用 SVGR:

SVGR CLI

# Install SVGR
npm install -D @svgr/cli

# Convert a single SVG file to a React component
npx @svgr/cli --icon --typescript icon.svg

# Convert an entire directory of SVGs
npx @svgr/cli --icon --typescript --out-dir src/components/icons src/assets/svg/

# With SVGO optimization included
npx @svgr/cli --icon --svgo --typescript icon.svg

SVGR with webpack (React/Next.js)

// next.config.js (Next.js example)
module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: [
        {
          loader: '@svgr/webpack',
          options: {
            svgo: true,
            svgoConfig: {
              plugins: [
                { name: 'removeViewBox', active: false },
                { name: 'removeDimensions', active: true },
              ],
            },
            titleProp: true,    // adds title prop for a11y
            typescript: true,   // generates TypeScript
          },
        },
      ],
    });
    return config;
  },
};

// Usage in your component:
import ArrowIcon from './arrow.svg';

function Button() {
  return (
    <button>
      <ArrowIcon width={20} height={20} title="Navigate" />
      Next
    </button>
  );
}

SVGR with Vite

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';

export default defineConfig({
  plugins: [
    react(),
    svgr({
      svgrOptions: {
        icon: true,
        typescript: true,
        svgo: true,
      },
    }),
  ],
});

// Usage with named import:
import { ReactComponent as Logo } from './logo.svg';
// or default import depending on config:
import Logo from './logo.svg?react';

SVGR 通过 .svgrrc.json 支持广泛配置,包括默认导出类型、TypeScript 支持、可访问性标题等。

.svgrrc.json configuration

{
  "icon": true,
  "typescript": true,
  "svgo": true,
  "titleProp": true,
  "replaceAttrValues": {
    "#000": "currentColor",
    "#000000": "currentColor"
  },
  "svgoConfig": {
    "plugins": [
      { "name": "removeViewBox", "active": false },
      { "name": "removeDimensions", "active": true },
      { "name": "removeTitle", "active": false }
    ]
  },
  "template": null
}

内联 SVG 与外部 SVG 文件

在 React 中使用 SVG 有两种主要方式,各有权衡:

内联 SVG(作为 JSX 组件)

  • 优点:完全 CSS/JS 控制、动态 props、可 tree-shake、无额外 HTTP 请求
  • 缺点:增加 JS 包大小、不能独立缓存
  • 适合:图标、logo、小型插图(5KB 以下)

外部 SVG 文件(通过 img 标签或 CSS)

  • 优点:独立缓存、更小的 JS 包、浏览器原生渲染
  • 缺点:无动态样式、额外 HTTP 请求、无 JS 交互
  • 适合:大型插图、复杂图表、装饰性背景

混合方式效果最佳:交互图标用内联 SVG 组件,大型装饰图形用外部文件。阈值大约是 5KB。

// Inline SVG as component (best for icons)
import SearchIcon from '@/icons/SearchIcon';

function SearchBar() {
  return (
    <div className="search-bar">
      <SearchIcon size={20} color="var(--text-muted)" />
      <input type="text" placeholder="Search..." />
    </div>
  );
}

// External SVG via img tag (best for large illustrations)
function HeroSection() {
  return (
    <section>
      <h1>Welcome</h1>
      <img
        src="/illustrations/hero-graphic.svg"
        alt="Developer tools illustration"
        width={600}
        height={400}
        loading="lazy"
      />
    </section>
  );
}

SVG 在 React 中的无障碍最佳实践

使 SVG 组件可访问需要注意几个关键细节。装饰性 SVG 应对屏幕阅读器隐藏,有意义的 SVG 需要适当的标签。

// Meaningful SVG - conveys information
const WarningIcon = ({ message }: { message: string }) => (
  <svg
    role="img"
    aria-label={message}
    viewBox="0 0 24 24"
    width={24}
    height={24}
  >
    <title>{message}</title>
    <path d="M12 2L1 21h22L12 2zm0 4l7.5 13h-15L12 6z" fill="currentColor" />
    <path d="M11 10h2v5h-2zm0 6h2v2h-2z" fill="currentColor" />
  </svg>
);

// Decorative SVG - hidden from assistive technology
const DividerIcon = () => (
  <svg
    aria-hidden="true"
    focusable="false"
    viewBox="0 0 100 2"
    width={100}
    height={2}
  >
    <line x1="0" y1="1" x2="100" y2="1" stroke="currentColor" />
  </svg>
);

SVG 在 React 中的关键无障碍规则:

  • 为有意义的 SVG 添加 role="img"aria-label
  • 为装饰性 SVG 添加 aria-hidden="true"
  • 在 SVG 内部包含 <title> 元素作为备用描述
  • 使用 focusable="false" 防止键盘聚焦到装饰性 SVG
  • 确保 SVG 图标有足够的颜色对比度(WCAG 2.1 要求 3:1)

性能优化技巧

如果处理不当,SVG 组件可能影响性能。以下策略可以保持 React 应用的速度:

转换前优化 SVG:使用 SVGO 删除不必要的元数据,可减少 30-70% 的大小。

对图标组件使用 React.memo:防止不必要的重新渲染。

延迟加载大型 SVG 集:使用 React.lazy() 进行代码分割。

对重复图标使用 SVG 雪碧图:通过 <use> 引用更高效。

优先使用 currentColor:让 SVG 继承父元素的文本颜色。

// Memoized icon component - prevents unnecessary re-renders
import React from 'react';

interface IconProps extends React.SVGProps<SVGSVGElement> {
  size?: number;
}

const StarIcon = React.memo<IconProps>(({ size = 24, ...props }) => (
  <svg
    width={size}
    height={size}
    viewBox="0 0 24 24"
    fill="currentColor"
    {...props}
  >
    <path d="M12 2l3.09 6.26L22 9.27l-5 4.87
             1.18 6.88L12 17.77l-6.18 3.25L7 14.14
             2 9.27l6.91-1.01L12 2z" />
  </svg>
));
StarIcon.displayName = 'StarIcon';

// Lazy-loaded icon set for code splitting
const ChartIcons = React.lazy(() => import('./ChartIconSet'));

function Dashboard() {
  return (
    <React.Suspense fallback={<div style={{ width: 24, height: 24 }} />}>
      <ChartIcons.BarChart size={32} />
    </React.Suspense>
  );
}

// SVG sprite with <use> for repeated icons
function SpriteIcon({ id, size = 24 }: { id: string; size?: number }) {
  return (
    <svg width={size} height={size} aria-hidden="true">
      <use href={`/icons/sprite.svg#${id}`} />
    </svg>
  );
}

SVG 组件的 TypeScript 支持

使用 TypeScript 时,正确的类型定义确保 SVG 组件与代码库无缝集成:

// types/svg.d.ts - Enable SVG imports as React components
declare module '*.svg' {
  import React from 'react';
  const SVGComponent: React.FC<React.SVGProps<SVGSVGElement>>;
  export default SVGComponent;
}

// For Vite with vite-plugin-svgr:
declare module '*.svg?react' {
  import React from 'react';
  const SVGComponent: React.FC<React.SVGProps<SVGSVGElement>>;
  export default SVGComponent;
}

// Reusable icon component type
interface IconComponentProps extends React.SVGProps<SVGSVGElement> {
  size?: number;
  color?: string;
  title?: string;
}

// Icon factory function with proper types
function createIcon(
  path: string,
  displayName: string
): React.FC<IconComponentProps> {
  const Icon: React.FC<IconComponentProps> = ({
    size = 24,
    color = 'currentColor',
    title,
    ...props
  }) => (
    <svg
      width={size}
      height={size}
      viewBox="0 0 24 24"
      fill="none"
      stroke={color}
      strokeWidth={2}
      strokeLinecap="round"
      strokeLinejoin="round"
      role={title ? 'img' : undefined}
      aria-label={title}
      aria-hidden={!title}
      {...props}
    >
      {title && <title>{title}</title>}
      <path d={path} />
    </svg>
  );
  Icon.displayName = displayName;
  return React.memo(Icon);
}

可以为 SVG 导入生成 .d.ts 声明文件。

Next.js 项目中的 SVG

Next.js 需要使用 @svgr/webpack 进行特定配置:

// next.config.ts (Next.js 13+ with App Router)
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  webpack(config) {
    // Find the existing rule that handles SVG imports
    const fileLoaderRule = config.module.rules.find(
      (rule: { test?: RegExp }) => rule.test?.test?.('.svg')
    );

    config.module.rules.push(
      // Reapply the existing rule, but only for svg imports
      // ending in ?url (e.g. import iconUrl from './icon.svg?url')
      {
        ...fileLoaderRule,
        test: /\.svg$/i,
        resourceQuery: /url/,
      },
      // Convert all other *.svg imports to React components
      {
        test: /\.svg$/i,
        issuer: fileLoaderRule.issuer,
        resourceQuery: {
          not: [...(fileLoaderRule.resourceQuery?.not || []), /url/],
        },
        use: [
          {
            loader: '@svgr/webpack',
            options: {
              typescript: true,
              svgo: true,
              titleProp: true,
            },
          },
        ],
      }
    );

    // Modify the file loader rule to ignore *.svg
    fileLoaderRule.exclude = /\.svg$/i;

    return config;
  },
};

export default nextConfig;

配置后可在 Next.js 页面中直接将 SVG 导入为 React 组件,webpack 加载器在构建时处理转换。

常见问题

如何将 SVG 转换为 React 组件?

通过将 HTML 属性转换为 JSX 等价属性、包装在函数组件中并导出来转换。使用 SVGR 进行自动化转换,或使用我们的免费在线工具快速转换。

SVG to JSX 和 SVG to React 有什么区别?

SVG to JSX 转换属性语法使其成为有效的 JSX。SVG to React 更进一步,将 JSX 包装在带有 props 支持和 TypeScript 类型的 React 组件中。

应该在 React 中内联 SVG 还是使用 img 标签?

5KB 以下需要样式控制的用内联 SVG 组件,大型装饰性 SVG 用 img 标签。混合方式通常最佳。

如何使 SVG 组件可访问?

有意义的 SVG 添加 role="img" 和 aria-label,装饰性 SVG 添加 aria-hidden="true" 和 focusable="false"。

可以在 React Native 中使用 SVG 吗?

可以,但需要 react-native-svg 库。SVGR 也有 React Native 模式生成兼容组件。

SVG 转换为 React 组件让您兼得两全:分辨率无关的图形和完整的 React 交互性。快速转换使用在线工具,项目级 SVG 处理使用 SVGR。始终先用 SVGO 优化 SVG,并遵循无障碍最佳实践。

使用我们的免费在线工具将 SVG 转换为 JSX。

Related Developer Tools and Guides

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

SVGSVG to JSX/ReactJSXHTML to JSX

相关文章

React 中的 SVG:从原始 SVG 到优化组件

学习将原始 SVG 转换为高效的 React 组件。涵盖 SVGO 优化、无障碍访问、动画和 TypeScript props。

SVG viewBox 详解:宽度、高度、缩放与响应式 SVG

揭秘 SVG viewBox 属性。了解 min-x、min-y、width 和 height 如何控制坐标系统,以及如何让 SVG 完全响应式。

HTML 转 JSX:React 迁移所需的一切

全面的 HTML 转 JSX for React 指南。涵盖 className、style 对象、自闭合标签、事件处理和常见陷阱。