Open emlai opened 2 years ago
@emlai SVGR transforms SVG icons to React components, how you expect you react component to look like in the output?
I don't think it's possible with this plugin. you can write your babel-plugin to transform svg to empty tag https://react-svgr.com/docs/custom-transformations/
I solved this by defining a custom babel plugin to remove the parent <svg>
element (added under
jsx.babelConfig.plugins
):
const removeSvgElement = (): PluginObj => ({
visitor: {
JSXElement(path) {
if ((path.node.openingElement.name as JSXIdentifier).name === "svg") {
if (path.node.children.length === 1) {
path.replaceWith(path.node.children[0]);
} else {
path.replaceWith(
t.jsxFragment(
t.jsxOpeningFragment(),
t.jsxClosingFragment(),
path.node.children
)
);
}
}
},
},
});
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@emlai Thank you so much! Saved me a ton of time
@headfire94 To provide more context, the goal here is to replace top <svg>
element with a more flexible React component, e.g.
const iconTemplate: Template = ({ componentName, jsx }, { tpl }) => {
return tpl`/* Generated file. Do not modify. */
import { IconSvg, type IconSvgProps } from '../../IconSvg';
export const ${componentName} = (props: IconSvgProps) => <IconSvg size={props.size}>${jsx}</IconSvg>;
`;
};
Here is a full plugin file to save a few minutes for the next person:
import { type JSXIdentifier, type PluginObj, types as t } from '@babel/core';
/**
* Babel plugin for svgr that removes svg element.
*
* @see https://react-svgr.com/docs/custom-transformations/
* @author https://github.com/gregberge/svgr/issues/769#issuecomment-1236126769
*/
export const removeSvgElement = (): PluginObj => ({
visitor: {
JSXElement(path) {
if ((path.node.openingElement.name as JSXIdentifier).name === 'svg') {
if (path.node.children.length === 1) {
path.replaceWith(path.node.children[0]);
} else {
path.replaceWith(
t.jsxFragment(
t.jsxOpeningFragment(),
t.jsxClosingFragment(),
path.node.children,
),
);
}
}
},
},
});
Just realized there is a better way of doing this: just reference jsx.children
const iconTemplate: Template = ({ componentName, jsx }, { tpl }) => {
return tpl`/* Generated file. Do not modify. */
import { createElement } from 'react';
import { IconSvg, type IconSvgProps } from '../../IconSvg';
export const ${componentName} = (props: IconSvgProps) => createElement(SvgIcon, props, ${jsx.children});
`;
};
gajus' solution using ${jsx.children}
doesn't work in my case, I get an error from @babel/template
:
@babel/template placeholder "$1": Cannot replace single expression with an array.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
gajus' solution using
${jsx.children}
doesn't work in my case, I get an error from@babel/template
:@babel/template placeholder "$1": Cannot replace single expression with an array.
Same here. I have to use the follow code (source) instead of just ${jsx.children}
to fix that error.
${
jsx.children.length === 1
? jsx.children[0]
: {
type: "JSXFragment",
openingFragment: {
type: "JSXOpeningFragment",
},
closingFragment: {
type: "JSXClosingFragment",
},
children: jsx.children,
}
}
We need to transform our icons to the following format, i.e. without the parent
<svg>
element:By default svgr includes the
<svg>
element in the output. How can this be done in svgr?