react-sass-inlinesvg
v1.0.5
Published
React library designed to control SVG from Sass.
Downloads
2,413
Maintainers
Readme
react-sass-inlinesvg
This is a React library that allows you to control inline SVG (SVG in HTML) from Sass.
This library was inspired by a great library called react-inlinesvg.
It has almost the same functionality as react-inlinesvg, but with more convenience and flexibility.
View the demo
Highlight
- 🏖 Easy to use:Just use the mixins provided
- 🛠 Flexible: Various controls are available from Sass
- 🚀 Performance: Faster speeds with a more optimized cache mechanism
- 📌 SSR: Avoid layout deviations due to initial display elements
- 🟦 Typescript: Nicely typed
| Function | react-sass-inlinesvg | react-inlinesvg | smart-svg | | -------------------------------------------------- | -------------------- | ----------------- | ----------- | | Specify SVG in Sass | ✅ | ❌ | ✅ | | Specify SVG in JSX | ✅ | ✅ | ❌ | | Style control for individual child elements in SVG | ✅ | ✅ | ❌ | | SVG coloring | ✅ | ✅ | ✅ | | Circular and rectangular supports | ✅ | ❌ | ✅ | | SVG display for pseudo-elements | ❌ | ❌ | ✅ | | Use outside of React | ❌ | ❌ | ✅ | | IE11 Support | ✅ | ✅ | ❌ | | performance | A | C | A+ |
Articles on implementation innovations and performance details.
https://dwango.github.io/articles/2022-12_nicolive_svg/
The following will help you in selecting a library.
- react-sass-inlinesvg - This is useful when you want to apply different styles to individual child elements within an SVG element and want to specify which SVG to display from the Sass.
- react-inlinesvg - It is a stable library.
- smart-svg - This is the smartest way if it meets the functional requirements.
Installing
Yarn
yarn add react-sass-inlinesvg
npm
npm i react-sass-inlinesvg
Storybook
Yarn
yarn start
npm
npm run start
Using
Setup
To use react-sass-inlinesvg with the component name SVG, prepare the following configuration.
The "src/atoms" part is optional.
src
atoms
svg
_index.scss // Definition of mixin
_names.scss // SVG Name Definition
svg.stories.module.scss // Styles for Stories
Svg.stories.tsx // Story Definition
Svg.tsx // React Component
Paste the following source code into each file and rewrite only the specified sections.
src/atoms/svg/_index.scss
Set $css-modules
to true
only for CSS Modules.
@use "sass:meta";
@use "./names";
@forward "react-sass-inlinesvg" with ($css-modules: false, $svg-names: meta.module-variables("names"));
@forward "./names";
src/atoms/svg/_names.scss
Define variables for the available SVG names.
$React: "React"; // Variable name and value must be the same
$Sass: "Sass";
$Svg: "Svg";
src/atoms/svg/svg.stories.module.scss
Copy and paste the following verbatim (no changes necessary).
@use "." as *;
.svg-catalog {
@include story-catalog;
}
src/atoms/svg/Svg.stories.tsx
Copy and paste the following verbatim (no changes necessary).
import React from "react";
import { renderStoryCatalog } from "react-sass-inlinesvg";
import { SVG, pathMap } from "./Svg";
import classNames from "./svg.module.scss";
export default { component: SVG };
export const Catalog = () =>
renderStoryCatalog(SVG, pathMap, classNames.svgCatalog);
src/atoms/svg/Svg.tsx
Pass an object that defines the correspondence between SVG names and paths as the argument to setup()
.
import { setup, ExtractProps } from "react-sass-inlinesvg";
export type SVGProps = ExtractProps<typeof SVG>;
export const { SVG, pathMap } = setup({
React: () => "https://cdn.svgporn.com/logos/react.svg",
Sass: () => "https://cdn.svgporn.com/logos/sass.svg",
Svg: () => "https://cdn.svgporn.com/logos/svg.svg",
});
Launch Storybook and if you see SVG in the catalog, setup is complete.
Examples
Example of displaying SVG in a component called "Example."
src
atoms // atoms as in Setup example
...
example
example.module.scss // style definition
Example.stories.tsx // story definition
Example.tsx // component
src/templates/example/Example.tsx
- Place the
<SVG>
component - Pass
className
to the element
import React from "react";
import { SVG } from "../../atoms/svg/Svg";
import classNames from "./example.module.scss";
export const Example = () => {
return (
<div>
{/* Standard way to pass className to SVG. */}
<button className={classNames.reactButton}>
<SVG className={classNames.svg} /> React
</button>
<button className={classNames.sassButton}>
<SVG className={classNames.svg} /> Sass
</button>
{/* How to use SVG without passing className. */}
<button className={classNames.button}>
<SVG /> SVG
</button>
<button className={classNames.button}>
<SVG /> NULL
</button>
</div>
);
};
src/example/example.module.scss
- Refer to
_index.scss
insvg
with@use
. - Specify the SVG you want to display using
@include
in the selector for<SVG>
.
// In `@use`, you can omit "/_index.scss" and the string after the trailing slash becomes a namespace.
// Please refer to the official documentation for the usage of the `@use` namespace in Sass.
// https://sass-lang.com/documentation/at-rules/use#choosing-a-namespace
@use "../../atoms/svg";
.react-button {
font-size: 48px;
.svg {
@include svg.show(svg.$React, 0.8em); // Display the React logo
}
&:hover .svg {
@include svg.show(
svg.$Sass
); // Hover over the button to display the Sass logo
}
&:active .svg {
@include svg.show(svg.$Svg); // Show the SVG logo when the button is pressed
}
}
.sass-button {
font-size: 48px;
.svg {
@include svg.show(svg.$Sass, 0.8em); // Display the Sass logo
}
&:hover .svg {
@include svg.show(svg.$Svg);
}
&:hover .svg,
// Selector for preventing flickering.(Moment after the hover ends, when the SVG has not yet been rewritten)
&:not(:hover) .svg[data-svg-name="#{svg.$Svg}"] {
fill: gray; // SVG changes color when hovering over a button.
}
&:active .svg {
@include svg.none; // When the button is pressed, the element does not maintain its area and becomes invisible.(`display: none` equivalent)
}
}
.button {
font-size: 48px;
&:nth-of-type(3) {
> svg {
@include svg.show(svg.$Svg, 0.8em); // Display the Sass logo
}
&:hover > svg {
@include svg.hidden; // When the button is pressed, the element is made invisible while maintaining its area.(`visibility: hidden` equivalent)
}
&:active > svg {
@include svg.null; // When the button is pressed, the entire element disappears and is not restored.
}
}
&:nth-of-type(4) {
> svg {
@include svg.null; // Do not display the element itself(Note that there are elements that are not visible for a moment after the initial drawing.)
}
}
}
src/example/Example.stories.scss
- Add storybook stories.
import React from "react";
import { Example } from "./Example";
export default { component: Example };
export const Default = {};
If you start Storybook and the SVG is displayed, you have completed the usage check.
API(Sass)
| mixin | visibility | area | element | | ------------------ | ---------- | ---- | ------- | | svg.show | ✅ | ✅ | ✅ | | svg.hidden | ❌ | ✅ | ✅ | | svg.hidden-opacity | ❌ | ✅ | ✅ | | svg.none | ❌ | ❌ | ✅ | | svg.null | ❌ | ❌ | ❌ |
@include svg.show($svg-name, $args...) { /* content */ };
Displays the specified SVG.
$svg-name {string}
SVG name defined in src/atoms/svg/_names.scss
OR "HIDDEN" "HIDDEN-OPACITY" "NONE" "NULL".
$args
Equivalent to show()
in smart-svg, except that $url
argument can be used.
content
If necessary, CSS properties can be written within the block to add display during loading.
@include svg.show-circle($svg-name, $args...) { /* content */ };
The specified SVG is surrounded by a circular shape.
$svg-name {string}
SVG name defined in src/atoms/svg/_names.scss
OR "HIDDEN" "HIDDEN-OPACITY" "NONE" "NULL".
$args
Equivalent to show-circle()
in smart-svg, except $url
and $fill-image
arguments can be used.
content
If necessary, CSS properties can be written within the block to add display during loading.
@include svg.show-square($svg-name, $args...) { /* content */ };
The specified SVG is surrounded by a rectangle shape.
$svg-name {string}
SVG name defined in src/atoms/svg/_names.scss
OR "HIDDEN" "HIDDEN-OPACITY" "NONE" "NULL".
$args
Equivalent to show-square()
in smart-svg, except $url
and $fill-image
arguments can be used.
content
If necessary, CSS properties can be written within the block to add display during loading.
@include svg.hidden($size: null);
Like visibility: hidden
, it will be invisible with the area of the element reserved.
It will not respond to :hover
pseudo-selectors.
$size {string} ▶︎ null
The value used for the width
height
CSS property.
If omitted, width
height
will not be set.
@include svg.hidden-opacity($size: null);
Use opacity: 0
to make the element invisible with the area of the element reserved.
It also responds to :hover
pseudo-selectors.
$size {string} ▶︎ null
The value used for the width
height
CSS property.
If omitted, width
height
will not be set.
@include svg.none($size: null);
Like display: none
, the element is hidden with no area.
$size {string} ▶︎ null
The value used for the width
height
CSS property.
If omitted, width
height
will not be set.
@include svg.null;
The <svg>
element itself will not be output, just as if you had returned null
in a React component.
**However, the <svg>
element will not be visible after that. **
Note that if you specify @include svg.null
from the CSS selector side, an invisible element will be drawn for a moment.
This may affect +
:first-child
:last-child
:nth-*
:empty
, etc.
@include story-catalog;
This is a mixin that provides styles for the Story catalog.
API(tsx)
setup(pathMap, options)
When the SVG component sets the available SVG information, it returns the component and the pathMap passed as arguments.
pathMap {{[string]: () => string}}
A map of functions that return SVG names and paths.
{
FooIcon: () => "https://.../foo-icon.svg",
BarIcon: () => "https://.../bar-icon.svg",
}
options.fetchOptions {RequestInit}
Custom options for the request.
options.uniqueHash {string} ▶︎ a random 8 characters string [A-Za-z0-9]
A string to use with uniquifyIDs
.
options.uniquifyIDs {boolean} ▶︎ false
Create unique IDs for each icon.
ExtractProps
The type from which the Props type of the SVG component is extracted.
type SVGProps = ExtractProps<typeof SVG>;
renderStoryCatalog()
Function to draw a catalog of stories.
Props
Based on React.SVGProps<SVGSVGElement>
.
defaultName {string}
SVG name for initial rendering.
Available when there is no need to switch SVGs and no need to specify it on the Sass side.
If defaultName
is "NULL"
, unlike @include svg.null;
, the element is not output from the first drawing.
description {string}
A description for your SVG. It will override an existing <desc>
tag.
innerRef {React.Ref}
Set a ref in SVGElement.
onLoad {function}
A callback to be invoked upon successful load.
This will receive 2 arguments: the src
prop and a hasCache
boolean
onError {function}
A callback to be invoked if loading the SVG fails. This will receive a single argument with:
a FetchError
with:
{
message: string;
type: string;
errno: string;
code: string;
}
or an Error, which has the following properties:
{
message: string;
}
title {string}
A title for your SVG. It will override an existing <title>
tag.
How react-sass-inlinesvg works
- Draw
<svg>
with empty<svg>
.<svg className="svg" aria-busy="true"></svg>
- Add
<style>
containing@keyframes
to<head>
. - Start listening for animation.
<svg className="svg" aria-busy="true" data-svg-status="loading"></svg>
- The
animation
event of<svg>
element's::before
fires. - Event handling.
- Extract SVG names from
event.animationName
. - Resolve URL from SVG name and fetch
<svg className="svg" aria-busy="true" data-svg-status="loading" data-svg-name="FooIcon"></svg>
- Extract SVG names from
- Reflect the acquired SVG content in the element
<svg className="svg" data-svg-status="complete" data-svg-name="FooIcon">*</svg>
No change in ref
will occur as the state or content of the SVG changes, since we will always use a single svg element.
More Advanced Usage
Selector by state
If you want to control the style in detail according to the state of the SVG before it completes loading, please refer to the following.
@use "../../atoms/svg";
.svg {
@include svg.show(svg.$React) {
// The style described here will be used for display during loading.
}
&[aria-busy="true"] {
// After drawing the element - before animationstart event listening starts.
&[data-svg-status="loading"] {
// After animationstart event listening starts - Before animationstart event processing.
&[data-svg-name] {
// After animationstart event is processed - before SVG content is reflected.
}
}
}
&[data-svg-status="complete"] {
// After reflecting SVG contents.
}
&[data-svg-status="error"] {
// on error.
}
}
Flicker Prevention
In react-sass-inlinesvg, there is a momentary time lag due to the mechanism of switching SVG via animationstart.
Therefore, in .sass-button {}
in the src/example/example.module.scss
example, if you try to change the color when the SVG switches on hover as shown below, the SVG and color switching timing will not match, causing a flicker.
.sass-button {
// ...
&:hover .svg * {
fill: gray; // SVG changes color when hovering over a button.
}
}
This can be handled by applying a style that specifies that the hover condition has changed and the SVG has not yet switched.
.sass-button {
// ...
&:hover .svg *,
// Selector for preventing flickering.(Moment after the hover ends, when the SVG has not yet been rewritten)
&:not(:hover) .svg[data-svg-name="#{svg.$Svg}"] * {
fill: gray; // SVG changes color when hovering over a button.
}
}
Browser Support
Any browsers that support inlining SVGs and fetch and animationstart will work.
If you need to support legacy browsers you'll need to include a polyfiil for fetch
and Number.isNaN
in your app. Take a look at react-app-polyfill or polyfill.io.
CORS
If you are loading remote SVGs, you'll need to make sure it has CORS support.
Why you need this package?
react-inlinesvgIn addition to the reasons given in why-you-need-this-package, it is beneficial when you want to control which SVGs are displayed from your Sass.
Using react-sass-inlinesvg provides the following benefits.
- Eliminates the need to write JS logic to switch between SVGs based on various conditions, such as
:hover
. - JSX improves component reusability by eliminating the need to determine specific SVGs.
- React processing costs are reduced by a design that cuts wasteful processing as much as possible.
- Significant performance differences, especially when displaying large numbers of SVGs.
- Significant performance differences, especially for large displays of overlapping SVGs.
Note
- When using react-sass-inlinesvg, SVG must be switched with Sass
- If I have two SVG components and switch between them in JSX, I have a problem with elements disappearing momentarily.