Make any SVG responsive with this React component - LogRocket Blog (2024)

If you are putting out digital content into the world wide web to publicize your work, then there is a high chance that your audience will view it on a mobile device. For text and images, this is not a huge problem with modern CSS. But if the content is a data visualization using SVG elements, this might be a problem.

Make any SVG responsive with this React component - LogRocket Blog (1)

I initially ran into problems on mobile devices with some graphs I was working on creating. After a lot of fumbling, I made some reusable components that can shield me from this pain.

How much space does the SVG have to grow?

There are a few abstractions to consider when working out how much space is available to scale an SVG document.

Make any SVG responsive with this React component - LogRocket Blog (2)

Browser viewport

The browser viewport is the visible area of a web page.

SVG viewport

The SVG viewport is analogous to the browser’s viewport only it is the visible area of an SVG document. An SVG document can be as high and wide as you want, but only part of the document can be visible at one time.

We want to avoid hardcoded width and height attributes like in the example below because they cannot adapt to the multitude of devices that might be viewing this document:

export const App: React.FC = () => ( <svg width="1000" height="1000"> <rect x="20%" y="20%" width="1000" height="1000" rx="20" style={{ fill: '#ff0000', stroke: '#000000', strokeWidth: '2px' }} /> <rect x="30%" y="30%" width="1000" height="1000" rx="40" style={{ fill: '#0000ff', stroke: '#000000', strokeWidth: '2px', fillOpacity: 0.7 }} /> </svg>);

The above component renders this document:

Make any SVG responsive with this React component - LogRocket Blog (3)

The above SVG document of 2 <rect/> elements is larger than the SVG viewport, and because of this, only part of it is visible.

A magical attribute called the viewBox is the answer to a lot of our SVG responsive needs.

viewBox and coordinate systems

The definition from mdn for the viewBox attribute is:

The viewBox attribute defines the position and dimension, in user space, of an SVG viewport.

I don’t know about you, but I had more questions than answers when I first read that. What on earth is user space?

If you have sat maths to any level, then you will have come across euclidean space with the classic x and y axes:

Make any SVG responsive with this React component - LogRocket Blog (4)

SVG coordinate space is different because the point (0, 0) is at the top left-hand corner and not the center.

If we add a viewBox attribute to the SVG that was created earlier, then we can regain control of the sizing of the SVG viewport:

export const App: React.FC = () => ( <svg preserveAspectRatio="xMaxYMid meet" viewBox="0 0 529 470"> <rect x="20%" y="20%" width="1000" height="1000" rx="20" style={{ fill: '#ff0000', stroke: '#000000', strokeWidth: '2px' }} /> <rect x="30%" y="30%" width="1000" height="1000" rx="40" style={{ fill: '#0000ff', stroke: '#000000', strokeWidth: '2px', fillOpacity: 0.7 }} /> </svg>);

The two <rect/> elements are now smaller than the SVG viewport thanks to this magical attribute.

Over 200k developers use LogRocket to create better digital experiencesLearn more →

Make any SVG responsive with this React component - LogRocket Blog (7)

viewBox math

Below is an example of a viewBox attribute:

viewBox="-200 -100 3000 400"

The magical four numbers in the attribute can shrink or expand the elements and transform their position. But how?

The viewBox does a lot with very little, such as:

  • It defines the aspect ratio of the image
  • It defines how all the lengths and coordinates used inside the SVG should be scaled to fit the space available
  • It defines the origin of the new coordinate system

I like to remember the initial four values like this:

viewBox="minX minY width height"

A new set of coordinates are copied from the existing SVG viewport coordinates with the addition of the viewBox. This new set of coordinates is known as a user space. The mdn cryptic explanation mentioned this:

The viewBox attribute defines the position and dimension, in user space, of an SVG viewport.

The first two values of the viewBox are -200 and -100, and these values will shift the image right and down from the top-left origin:

<svg width="500px" height="100px" viewBox="-200 -100 3000 400">

The last two values, 3000 and 400, allow us to zoom into or away from our SVG image.

The SVG element has a width of 500px and a height of 100px. With the addition of the viewBox attribute, a new user coordinate system of 3000 units and 400 hundred units vertically is at our disposal.

The new user space maps to the viewport coordinate system, and 1 unit of the new space is equal to this calculation:

height = SVG viewport height ÷ viewBox height verticallywidth = SVG viewport width ÷ viewBox width horizontally

Make any SVG responsive with this React component - LogRocket Blog (8)
The viewBox on its own will not cure all our responsive needs. We cannot use hardcoded values in the real world. We need to get new values when the component is mounted, or it resizes.

Responsive SVG

Below is my ResponsiveSVG component from my very own @cutting/svg package:

export function ResponsiveSVG<E extends HTMLElement = HTMLElement>({ elementRef, children, preserveAspectRatio = 'xMinYMin slice', innerRef, className, options = {},}: PropsWithChildren<ParentsizeSVGProps<E>>): JSX.Element | null { const { width, height } = useParentSize(elementRef, options); const aspect = width / height; const adjustedHeight = Math.ceil(width / aspect); return ( <div data-selector="cutting-svg-container" style={{ position: 'relative', overflow: 'visible', height: '1px', }} > <svg style={{ overflow: 'visible' }} className={className} preserveAspectRatio={preserveAspectRatio} viewBox={`0 0 ${width} ${adjustedHeight}`} ref={innerRef} > {children} </svg> </div> );}

The above component makes use of my useParentSize hook that uses a ResizeObserver to watch for changes in a container <div/> element. Any time a change of dimensions happens in the target <div />, a resize event raises new width and height values and the component will re-render in terms of these new values.

To ensure our SVG element keeps its shape at different sizes, we need to calculate its height in terms of its width. This proportional relationship is known as the aspect ratio.

Aspect ratio

Aspect ratio is the ratio of width to height of an image on a screen.

A square image has an aspect ratio of 1:1. An image that is twice as tall as it is wide has an aspect ratio of 1:2.

The calculation for the adjusted height is:

const aspect = width / height;const adjustedHeight = Math.ceil(width / aspect);

These values help us complete the viewBox:

<svg style={{ overflow: 'visible' }} className={className} preserveAspectRatio={preserveAspectRatio} viewBox={`0 0 ${width} ${adjustedHeight}`} ref={innerRef}>

The preserveAspectRatio attribute

The viewBox attribute has a sidekick, preserveAspectRatio. It does not have any effect unless the viewBox exists. The preserveAspectRatio describes how an SVG document should scale if the aspect ratio of the viewBox does not match the aspect ratio of the viewPort. Most of the time, the default behavior works, which is:

preserveAspectRatio="xMidYMid meet"

xMidYMid meet is a bit like the CSS rule background-size: contain;. A slice value will scale the image to fit the more generous dimensions and slice off the extra.

preserveAspectRatio="xMidYMid slice"

The slice value is analogous to the overflow: hidden CSS rule.

Make any SVG responsive with this React component - LogRocket Blog (9)

Epilogue

With my ResponsiveSVG component, I am armed and ready for any device userland can throw at me, and below is how it looks when I resize my browser to something minimal:

Make any SVG responsive with this React component - LogRocket Blog (10)

Get set up with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to getan app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, notserver-side

    • npm
    • Script tag
    $ npm i --save logrocket // Code:import LogRocket from 'logrocket'; LogRocket.init('app/id'); 
    // Add to your HTML:<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script><script>window.LogRocket && window.LogRocket.init('app/id');</script> 
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin

Get started now

Make any SVG responsive with this React component - LogRocket Blog (2024)
Top Articles
Does a HELOC Affect Your Credit Score?
A Comprehensive Guide to Series A and Series B Valuations
Devotion Showtimes Near Xscape Theatres Blankenbaker 16
Nullreferenceexception 7 Days To Die
Somboun Asian Market
Shoe Game Lit Svg
Alan Miller Jewelers Oregon Ohio
From Algeria to Uzbekistan-These Are the Top Baby Names Around the World
Wmu Course Offerings
THE 10 BEST Women's Retreats in Germany for September 2024
Call Follower Osrs
Unlocking the Enigmatic Tonicamille: A Journey from Small Town to Social Media Stardom
Trade Chart Dave Richard
Www Thechristhospital Billpay
Nier Automata Chapter Select Unlock
Unterwegs im autonomen Freightliner Cascadia: Finger weg, jetzt fahre ich!
Gia_Divine
Lola Bunny R34 Gif
Apple Original Films and Skydance Animation’s highly anticipated “Luck” to premiere globally on Apple TV+ on Friday, August 5
Doki The Banker
Menus - Sea Level Oyster Bar - NBPT
Rochester Ny Missed Connections
Teekay Vop
From This Corner - Chief Glen Brock: A Shawnee Thinker
Busted Mugshots Paducah Ky
Gma' Deals & Steals Today
Eegees Gift Card Balance
What Is Opm1 Treas 310 Deposit
Promatch Parts
Jt Closeout World Rushville Indiana
Smayperu
Acuity Eye Group - La Quinta Photos
Hattie Bartons Brownie Recipe
Arcane Odyssey Stat Reset Potion
Google Jobs Denver
SOC 100 ONL Syllabus
450 Miles Away From Me
Vivek Flowers Chantilly
Trap Candy Strain Leafly
Check From Po Box 1111 Charlotte Nc 28201
5A Division 1 Playoff Bracket
6576771660
The Sports Academy - 101 Glenwest Drive, Glen Carbon, Illinois 62034 - Guide
The Average Amount of Calories in a Poke Bowl | Grubby's Poke
Leland Westerlund
Dayton Overdrive
Stephen Dilbeck, The First Hicks Baby: 5 Fast Facts You Need to Know
Upcoming Live Online Auctions - Online Hunting Auctions
Used Auto Parts in Houston 77013 | LKQ Pick Your Part
Craigslist Charlestown Indiana
Bloons Tower Defense 1 Unblocked
7 National Titles Forum
Latest Posts
Article information

Author: Arielle Torp

Last Updated:

Views: 5872

Rating: 4 / 5 (41 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Arielle Torp

Birthday: 1997-09-20

Address: 87313 Erdman Vista, North Dustinborough, WA 37563

Phone: +97216742823598

Job: Central Technology Officer

Hobby: Taekwondo, Macrame, Foreign language learning, Kite flying, Cooking, Skiing, Computer programming

Introduction: My name is Arielle Torp, I am a comfortable, kind, zealous, lovely, jolly, colorful, adventurous person who loves writing and wants to share my knowledge and understanding with you.