react-svg-patterns v0.7.0
React SVG Pattern Manager
Create, manage, and use SVG patterns in your React components!
Install
yarn:
yarn add react-svg-patternsnpm:
npm install --save react-svg-patternsUsage
Import createManagedSvgPatternLibrary and use it to create a ManagedSvgPatternLibrary component
and its accompanying registerSvgPattern function and useSvgPattern hook.
import { createManagedSvgPatternLibrary } from 'react-svg-patterns';
const {
ManagedSvgPatternLibrary,
registerSvgPattern,
useSvgPattern,
} = createManagedSvgPatternLibrary();Use registerSvgPattern to add SVG patterns to the library. The return value is a string that you
can use to reference the fill in your other SVGs.
const linearGradientFill = registerSvgPattern('myGradient', 'linear', {
angle: 30,
stops: ['#f00', '#0f0', '#00f'],
});
// linearGradientFill == 'url(#svg-pattern-linear__myGradient)'
const YourSvgIcon = () => (
<svg
width={100}
height={100}
viewBox="0 0 100 100"
fill={linearGradientFill}
>
<rect x={0} y={0} width={100} height={100} />
</svg>
);Note: Calling registerSvgPattern mid-render can cause problems for React. If you are creating
an SVG pattern inside a component during rendering, try useSvgPattern instead.
const YourSvgIcon = () => {
const linearGradientFill = useSvgPattern('myGradient', 'linear', {
angle: 30,
stops: ['#f00', '#0f0', '#00f'],
});
return (
<svg
width={100}
height={100}
viewBox="0 0 100 100"
fill={linearGradientFill}
>
<rect x={0} y={0} width={100} height={100} />
</svg>
);
};Add ManagedSvgPatternLibrary near the top of your component tree. This renders an SVG element
that acts as a container for all of your fill patterns.
const YourApp = () => (
<>
<div>
<YourSvgIcon />
</div>
<ManagedSvgPatternLibrary />
</>
);The rendered HTML ends up looking something like this:
<div>
<svg
width="100"
height="100"
viewBox="0 0 100 100"
fill="url(#svg-pattern-linear__myGradient)"
>
<rect x="0" y="0" width="100" height="100" />
</svg>
</div>
<svg style="bottom: 0px; height: 1px; opacity: 0; pointer-events: none; position: fixed; right: 0px; width: 1px; z-index: -1;">
<linearGradient id="svg-pattern-linear__myGradient" x1="0.5" x2="0.5" y1="1" y2="0" spreadMethod="pad">
<stop offset="0%" stop-color="#f00"></stop>
<stop offset="50%" stop-color="#0f0"></stop>
<stop offset="100%" stop-color="#00f"></stop>
</linearGradient>
</svg>API
SvgPatternManager
add(key, type, params)
Registers a new pattern fill.
Parameters
key
Unique ID for the pattern you're adding.
type and params
Determines the type of SVG pattern. All patterns types accept a string for id, but other
parameters are optional and vary by pattern type.
linear (PATTERN_TYPE_LINEAR_GRADIENT, LinearGradient)
stops: array of colors or{ offset, color }fromandto: shorthand for a gradient with two stopsangle: gradient angle in degrees (default:0, top-to-bottom)spreadMethodscale: enlarge the gradient within the pattern
radial (PATTERN_TYPE_RADIAL_GRADIENT, RadialGradient)
stops: array of colors or{ offset, color }fromandto: shorthand for a gradient with two stopsr: gradient radiuscxandcy: center of the gradientfxandfy: focus of the gradientspreadMethodscale: enlarge the gradient within the pattern
angular (PATTERN_TYPE_ANGULAR_GRADIENT, AngularGradient)
stops: array of colors or{ offset, color }fromandto: shorthand for a gradient with two stopsangle: gradient start angle in degrees (default:0, clockwise from the top)slices: number of slices in the gradient, lower is chunkier (default:100)spreadMethodscale: enlarge the gradient within the pattern
image (PATTERN_TYPE_IMAGE, BackgroundImage)
src: URL of the image
custom (PATTERN_TYPE_CUSTOM, CustomSvgPattern)
children: SVG content to use in<pattern>or a function that creates it
Returns
Pattern URL string that can be used as a fill for other SVGs.
get(key)
Returns the pattern URL for a given key.
Parameters
key
Unique ID for the pattern.
Returns
Pattern URL string that can be used as a fill for other SVGs.
has(key)
Check whether a key was registered for a pattern.
Parameters
key
Unique ID for the pattern.
Returns
true if the key was used to register a pattern or false otherwise
remove(key)
Removes the pattern fill for the given key.
Parameters
key
Unique ID for the pattern.
SvgPatternLibrary
Component
Props
patterns
Object mapping keys to SVG pattern definition objects with an id, type, and props.
noSvgWrapper
If set, this will render the patterns without a wrapping <svg> tag.
(default: false)
createManagedSvgPatternLibrary([idPrefix|patternManager])
Creates a function to register SVG patterns and a component to render them.
Parameters
idPrefix
A new SvgPatternManager instance is created with this idPrefix. The prefix is added to the
beginning each pattern element's id attribute.
(string, default: 'svg-pattern')
patternManager
You can also supply a SvgPatternManager instance directly.
Returns
ManagedSvgPatternLibrary: React component that renders the SVG patterns registered to theSvgPatternManagerinstancegetSvgPattern(key): gets the fill URL for the supplied keyregisterSvgPattern(key, type, params): registers an SVG patternuseSvgPattern(key, type, params): hook version ofregisterSvgPattern
createSvgPatternManager(idPrefix)
Creates a singleton SvgPatternManager instance with functions to register and retrieve
url(#...) strings that can be used as the fill for your SVGs.
Parameters
idPrefix
A new SvgPatternManager instance is created with this idPrefix.
(string, default: 'svg-pattern')
Returns
getInstance(): returns theSvgPatternManagerinstancegetSvgPattern(key): gets the fill URL for the supplied keyregisterSvgPattern(key, type, params): registers an SVG patternuseSvgPattern(key, type, params): hook version ofregisterSvgPattern
Tips and Tricks
Sizing and Positioning
These patterns stretch to fill the shape when they're applied. Aspect ratio is not preserved!
For multiple shapes or paths in a single SVG, the fill pattern applies to each one individually.
In other words, there is a separate copy of the pattern tht starts at the top-left of the shape and
stretches to the bottom-right. If you want one continous fill, you need to merge the shapes in your
SVG down to a single <path> first.
If you want to ensure that the pattern is always oriented to the top-left of your SVG, you may want to use a mask or a local pattern instead.
const YourSvgIcon = ({ fill }) => {
const localPatternId = useMemo(() => `pattern-${Math.random().toString().substr(2)}`, []);
return (
<svg fill={fill}>
<defs>
<pattern id={localPatternId} x={0} y={0} width="100%" height="100%" patternUnits="userSpaceOnUse" patternContentUnits="userSpaceOnUse">
<rect fill="inherit" x={0} y={0} width="100%" height="100%" />
</pattern>
</defs>
<g fill={`url(#${localPatternId})`}>
{/* shapes, etc. */}
</g>
</svg>
);
};Animation
You can animate fill patterns! However, be aware that Safari doesn't handle animations referenced across separate SVGs, as they would be if you created an animation in a custom pattern.
The destination (your SVG) does not update on every frame of the source (SVG pattern library), meaning that the pattern will not move at all or will update irregularly due to other incidental repaints, such as from hover events and window resizing.
There are a few work-arounds that may be helpful, depending on your needs:
- Create a static pattern in the library that is referenced by an animated pattern in your SVG
- Create the entire animated pattern in your SVG using this library's pattern components directly
Include a dummy animation in your SVG to force frequent repaints. This is very hacky and probably bad for performance as well!
The included
NoopAnimationHackcomponent will apply the hack for Safari only.import { NoopAnimationHack } from 'react-svg-patterns'; const YourSvgIcon = () => ( <svg fill={yourAnimatedFill}> <NoopAnimationHack /> <rect x={0} y={0} width={100} height={100} /> </svg> );
"Cannot update a component while rendering a different component"
Registering an SVG pattern inside of a component introduces a render loop that surfaces as an error in newer versions of React:
Cannot update a component (
ManagedSvgPatternLibrary) while rendering a different component (YourSvgIcon). To locate the bad setState() call insideYourSvgIcon, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
This happens when registering the pattern causes the ManagedSvgPatternLibrary to re-render while
the parent of the component that registered the pattern is already in the middle rendering. Ouch.
You can avoid this by registering your SVG patterns outside React. If that is not an option
because your pattern is dynamic or otherwise tied to the lifecycle of a component, you should call
registerSvgPattern in an effect instead. This is effectively what the useSvgPattern hook does.
Contributing
Thank you for your interest! If you find a bug or want to add a new feature, open an issue or create a pull request, and we'll figure it out from there.
License
ISC © Keith McKnight