Skip to content

feat(webpack): svg loader rules have been updated #12646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
9 changes: 6 additions & 3 deletions docusaurus/docs/adding-images-fonts-and-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,24 @@ An alternative way of handling static assets is described in the next section.

> Note: this feature is available with `[email protected]` and higher, and `[email protected]` and higher.

One way to add SVG files was described in the section above. You can also import SVGs directly as React components. You can use either of the two approaches. In your code it would look like this:
One way to add SVG files was described in the section above, but you need to add `?url` as query string to import it as URL. You can also import SVGs directly as React components. You can use either of the two approaches. In your code it would look like this:

```js
import { ReactComponent as Logo } from './logo.svg';
import Logo from './logo.svg';
import logo from './logo.svg?url';

function App() {
return (
<div>
{/* Logo is an actual React component */}
<Logo />
{/* logo as url */}
<img src={logo} alt="logo" />
</div>
);
}
```

This is handy if you don't want to load SVG as a separate file. Don't forget the curly braces in the import! The `ReactComponent` import name is significant and tells Create React App that you want a React component that renders an SVG, rather than its filename.
This is handy if you don't want to load SVG as a separate file.

> **Tip:** The imported SVG React Component accepts a `title` prop along with other props that a `svg` element accepts. Use this prop to add an accessible title to your svg component.
50 changes: 13 additions & 37 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 17 additions & 16 deletions packages/babel-plugin-named-asset-import/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pluginTester.default({
pluginOptions: {
loaderMap: {
svg: {
ReactComponent: '@svgr/webpack?-svgo![path]',
ReactComponent: '[path]',
},
},
},
Expand All @@ -28,24 +28,24 @@ pluginTester.default({
output: 'import { Url as logo1 } from "logo";',
},
svgDefaultImport: {
code: 'import logo from "logo.svg";',
output: 'import logo from "logo.svg";',
code: 'import Logo from "logo.svg";',
output: 'import Logo from "logo.svg";',
},
svgNamedImport: {
code: 'import { logo } from "logo.svg";',
output: 'import { logo } from "logo.svg";',
},
svgReactComponentNamedImport: {
code: 'import { ReactComponent as logo } from "logo.svg";',
output:
'import { ReactComponent as logo } from "@svgr/webpack?-svgo!logo.svg";',
code: 'import { ReactComponent as Logo } from "logo.svg";',
output: 'import { ReactComponent as Logo } from "logo.svg";',
},
svgMultipleImport: {
code: 'import logo, { logoUrl , ReactComponent as Logo } from "logo.svg";',
code:
'import logoUrl from "logo.svg?url";\n' +
'import Logo from "logo.svg";',
output:
'import logo from "logo.svg";\n' +
'import { logoUrl } from "logo.svg";\n' +
'import { ReactComponent as Logo } from "@svgr/webpack?-svgo!logo.svg";',
'import logoUrl from "logo.svg?url";\n' +
'import Logo from "logo.svg";',
},
defaultExport: {
code: 'export default logo;',
Expand Down Expand Up @@ -81,18 +81,19 @@ pluginTester.default({
},
svgReactComponentNamedExport: {
code: 'export { ReactComponent as Logo } from "logo.svg";',
output:
'export { ReactComponent as Logo } from "@svgr/webpack?-svgo!logo.svg";',
output: 'export { ReactComponent as Logo } from "logo.svg";',
},
svgReactComponentExport: {
code: 'export { ReactComponent } from "logo.svg";',
output: 'export { ReactComponent } from "@svgr/webpack?-svgo!logo.svg";',
output: 'export { ReactComponent } from "logo.svg";',
},
svgMultipleExport: {
code: 'export { logoUrl , ReactComponent as Logo } from "logo.svg";',
code:
'export { logoUrl } from "logo.svg?url";\n' +
'export { ReactComponent as Logo } from "logo.svg";',
output:
'export { logoUrl } from "logo.svg";\n' +
'export { ReactComponent as Logo } from "@svgr/webpack?-svgo!logo.svg";',
'export { logoUrl } from "logo.svg?url";\n' +
'export { ReactComponent as Logo } from "logo.svg";',
},
},
});
4 changes: 2 additions & 2 deletions packages/cra-template-typescript/template/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import logo from './logo.svg';
import Logo from './logo.svg';
import './App.css';

function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Logo className="App-logo" title="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
Expand Down
4 changes: 2 additions & 2 deletions packages/cra-template/template/src/App.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import logo from './logo.svg';
import Logo from './logo.svg';
import './App.css';

function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Logo className="App-logo" title="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
Expand Down
2 changes: 1 addition & 1 deletion packages/react-scripts/config/jest/fileTransform.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = {
const pascalCaseFilename = camelcase(path.parse(filename).name, {
pascalCase: true,
});
const componentName = `Svg${pascalCaseFilename}`;
const componentName = `${pascalCaseFilename}`;
return `const React = require('react');
module.exports = {
__esModule: true,
Expand Down
43 changes: 22 additions & 21 deletions packages/react-scripts/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,30 +386,31 @@ module.exports = function (webpackEnv) {
},
},
{
test: /\.svg$/,
use: [
{
loader: require.resolve('@svgr/webpack'),
options: {
prettier: false,
svgo: false,
svgoConfig: {
plugins: [{ removeViewBox: false }],
},
titleProp: true,
ref: true,
},
},
{
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash].[ext]',
},
},
],
test: /\.svg$/i,
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
type: 'asset',
resourceQuery: /url/, // *.svg?url
},
{
test: /\.svg$/i,
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url
use: {
loader: require.resolve('@svgr/webpack'),
options: {
prettier: false,
svgo: false,
svgoConfig: {
plugins: [{ removeViewBox: false }],
},
titleProp: true,
ref: true,
},
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,7 @@ describe('Integration', () => {

it('svg inclusion', async () => {
doc = await initDOM('svg-inclusion');
expect(doc.getElementById('feature-svg-inclusion').src).toMatch(
/\/static\/media\/logo\..+\.svg$/
);
expect(doc.getElementById('feature-svg-inclusion').src).toBeDefined();
});

it('svg component', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import React from 'react';
import { ReactComponent as Logo } from './assets/logo.svg';
import Logo from './assets/logo.svg';

const SvgComponent = () => {
return <Logo id="feature-svg-component" />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ describe('svg component', () => {
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<SvgComponent />, div);
expect(div.textContent).toBe('logo.svg');
expect(div.innerHTML).toBe(
'<logo.svg id="feature-svg-component"></logo.svg>'
);
});

it('svg root element equals the passed ref', () => {
const div = document.createElement('div');
const someRef = React.createRef();
ReactDOM.render(<SvgComponentWithRef ref={someRef} />, div);
const svgElement = div.getElementsByTagName('svg');
const svgElement = div.getElementsByTagName('logo.svg');
expect(svgElement).toHaveLength(1);
expect(svgElement[0]).toBe(someRef.current);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#feature-svg-in-css {
background-image: url('./logo.svg');
background-image: url('./logo.svg?url');
}
Loading