admin管理员组

文章数量:1296338

I want to use svg as Component here are all steps that i did:

  • HomeIcon is a svg icon that i want to use it but i get this error: [ Server ] React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.

How can i fix this?

  • my next.config.mjs:

    import createNextIntlPlugin from "next-intl/plugin";
    const withNextIntl = createNextIntlPlugin();
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {
       webpack: (config) => {
      // SVG
      // Grab the existing rule that handles SVG imports
      const fileLoaderRule = config.module.rules.find((rule) =>
        rule.test?.test?.(".svg")
      );
    
      config.module.rules.push(
        // Reapply the existing rule, but only for svg imports ending in ?url
        {
          ...fileLoaderRule,
          test: /\.svg$/i,
          resourceQuery: /url/, // *.svg?url
        },
        // Convert all other *.svg imports to React components
        {
          test: /\.svg$/i,
          issuer: fileLoaderRule.issuer,
          resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, //     exclude if *.svg?url
          use: [
            {
              loader: "@svgr/webpack",
              options: {
                typescript: true,
                ext: "tsx",
              },
            },
          ],
        }
      );
    
      // Modify the file loader rule to ignore *.svg, since we have it handled now.
      fileLoaderRule.exclude = /\.svg$/i;
    
      return config;
       },
    };
    export default withNextIntl(nextConfig);
    
    
  • I create svg.d.ts in my root directory:

      declare module "*.svg" {
      import React from "react";
      const SVG: React.VFC<React.SVGProps<SVGSVGElement>>;
      export default SVG;
    }
    
    
  • I added svg.d.ts inside tsconfig.json

    "include": [
      "svg.d.ts",
      "next-env.d.ts",
      "**/*.ts",
      "**/*.tsx",
      ".next/types/**/*.ts",
      "next.config.mjs"
    ],
    
  • In my Component :

    import Navbar from "@/components/Navbar";
    import { FC, ReactNode, SVGProps } from "react";
    import HomeIcon from "@/assets/home.svg";
    import Image from "next/image";
    
    interface layoutProps {
    children: ReactNode;
    locale: string;
    }
    
    const Layout: FC<layoutProps> = ({ children, locale }) => {
       return (
         <div>
           <Navbar locale={locale} />
           {children}
    
        <div className="px-7 bg-white shadow-lg rounded-2xl fixed bottom-0 right-0 left-    0 border border-green-600  ">
          <div className="flex">
            <div className="flex-1 group">
              <a
                href="#"
                className="flex items-end justify-center text-center mx-auto px-4 pt-2 w-full text-gray-400 group-hover:text-green-800"
              >
                <span className="block px-1 pt-1 pb-1">
                  <HomeIcon className="w-6 h-6" /> 
                  <span className="block text-xs pb-2">Home</span>
                  <span className="block w-5 mx-auto h-1 group-hover:bg-green-800 rounded-full"></span>
                </span>
              </a>
            </div>
        </div>
      </div>
    );
    };
    
    export default Layout;
    

I want to use svg as Component here are all steps that i did:

  • HomeIcon is a svg icon that i want to use it but i get this error: [ Server ] React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.

How can i fix this?

  • my next.config.mjs:

    import createNextIntlPlugin from "next-intl/plugin";
    const withNextIntl = createNextIntlPlugin();
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {
       webpack: (config) => {
      // SVG
      // Grab the existing rule that handles SVG imports
      const fileLoaderRule = config.module.rules.find((rule) =>
        rule.test?.test?.(".svg")
      );
    
      config.module.rules.push(
        // Reapply the existing rule, but only for svg imports ending in ?url
        {
          ...fileLoaderRule,
          test: /\.svg$/i,
          resourceQuery: /url/, // *.svg?url
        },
        // Convert all other *.svg imports to React components
        {
          test: /\.svg$/i,
          issuer: fileLoaderRule.issuer,
          resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, //     exclude if *.svg?url
          use: [
            {
              loader: "@svgr/webpack",
              options: {
                typescript: true,
                ext: "tsx",
              },
            },
          ],
        }
      );
    
      // Modify the file loader rule to ignore *.svg, since we have it handled now.
      fileLoaderRule.exclude = /\.svg$/i;
    
      return config;
       },
    };
    export default withNextIntl(nextConfig);
    
    
  • I create svg.d.ts in my root directory:

      declare module "*.svg" {
      import React from "react";
      const SVG: React.VFC<React.SVGProps<SVGSVGElement>>;
      export default SVG;
    }
    
    
  • I added svg.d.ts inside tsconfig.json

    "include": [
      "svg.d.ts",
      "next-env.d.ts",
      "**/*.ts",
      "**/*.tsx",
      ".next/types/**/*.ts",
      "next.config.mjs"
    ],
    
  • In my Component :

    import Navbar from "@/components/Navbar";
    import { FC, ReactNode, SVGProps } from "react";
    import HomeIcon from "@/assets/home.svg";
    import Image from "next/image";
    
    interface layoutProps {
    children: ReactNode;
    locale: string;
    }
    
    const Layout: FC<layoutProps> = ({ children, locale }) => {
       return (
         <div>
           <Navbar locale={locale} />
           {children}
    
        <div className="px-7 bg-white shadow-lg rounded-2xl fixed bottom-0 right-0 left-    0 border border-green-600  ">
          <div className="flex">
            <div className="flex-1 group">
              <a
                href="#"
                className="flex items-end justify-center text-center mx-auto px-4 pt-2 w-full text-gray-400 group-hover:text-green-800"
              >
                <span className="block px-1 pt-1 pb-1">
                  <HomeIcon className="w-6 h-6" /> 
                  <span className="block text-xs pb-2">Home</span>
                  <span className="block w-5 mx-auto h-1 group-hover:bg-green-800 rounded-full"></span>
                </span>
              </a>
            </div>
        </div>
      </div>
    );
    };
    
    export default Layout;
    
Share Improve this question edited Feb 15 at 12:19 Mohammadreza Ataei asked Feb 11 at 21:03 Mohammadreza AtaeiMohammadreza Ataei 1242 silver badges18 bronze badges 1
  • See stackoverflow/questions/65945886/… – Robert Longson Commented Feb 11 at 21:23
Add a comment  | 

4 Answers 4

Reset to default 0

You can't use SVG directly as a component. Instead, you can use it by using the Next.js Image component.

    <Image
      src={HomeIcon}
      alt="Home Icon"
      width={180}
      height={38}
      unoptimized={true}
    />

you can return svg in the component this way:

const Svg = () => (
  <svg width="320" height="130" xmlns="http://www.w3./2000/svg">
    <rect width="300" height="100" x="10" y="10" style="fill:rgb(0,0,255);stroke-width:3;stroke:red" />
  </svg>

);

export default Svg;

in your next.config.mjs., you can exclude SVG from the existing file loader rule.

// Exclude SVG from the existing file loader rule
    if (fileLoaderRule) {
      fileLoaderRule.exclude = /\.svg$/i;
    }

full configuration here:

import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin();

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

    // Exclude SVG from the existing file loader rule
    if (fileLoaderRule) {
      fileLoaderRule.exclude = /\.svg$/i;
    }

    // Add a new rule for SVGR
    config.module.rules.push({
      test: /\.svg$/i,
      use: [
        {
          loader: "@svgr/webpack",
          options: {
            typescript: true,
          },
        },
      ],
    });

    return config;
  },
};

export default withNextIntl(nextConfig);

or

fix the typescript error by importing SVGProps and SVGSVGElement as follows:

import { FC, SVGProps } from "react";

const Svg: FC<SVGProps<SVGSVGElement>> = () => (
  <svg width="320" height="130" xmlns="http://www.w3./2000/svg">
    <rect width="300" height="100" x="10" y="10" style="fill:rgb(0,0,255);stroke-width:3;stroke:red" />
  </svg>

);

Either one should fix the issue.

If you want to use SVG as a React Component, first, you need to convert the SVG file into a React Component, you can use online tools like Svg2jsx

本文标签: reactjsHow to use SVG in NextjsStack Overflow