#react
#ux
#charts
#svg
#recharts
#tech
#guide
#tips
8 min read

Awesome React Charts Tips: Gradients, Overlays and Responsive SSR in Recharts

Andriy Obrizan

Everyone wants to see beautiful charts in their web application. Existing chart libraries are excellent and provide beautiful visualizations out of the box. But what if you’re willing to go a step further?

We consider Recharts to be one of the best React charting libraries with the only downside of quite a massive bundle size. It supports server-side rendering, responsive charts, many different chart types, and features. Based on famous and time-tested D3.js, it shares similar extensibility principles and gives you control over the whole rendering. We will share some of the advanced tips for responsive server-side rendering, gradient shadows, and overlays.

Gradient Shadows on Line Charts

For some design styles adding smooth shadows to the Line Chart might drastically improve the picture.

For filled types of charts, like Area Chart it’s quite easy:

<AreaChart {props}>
    <defs>
        <linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
            <stop offset="5%" stopColor="#129a74" stopOpacity={0.8}/>
            <stop offset="95%" stopColor="#129a74" stopOpacity={0}/>
        </linearGradient>
    </defs>
    ...
    <Area fillOpacity={1} fill="url(#colorUv)" {props} />
</AreaChart>

With the Line Chart, you have to use some tricks.

Leveraging Area Chart for Gradients

If gradients work out of the box with filled types of charts, why don’t just add an Area Chart with the same data only for the visuals?

<ComposedChart {props}>
    <defs>
        <linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
            <stop offset="5%" stopColor="#129a74" stopOpacity={0.1}/>
            <stop offset="95%" stopColor="#FFFFFF" stopOpacity={0.1}/>
        </linearGradient>
    </defs>
    ...
    <Line dataKey="close" {props} />
    <Area type="monotone" dataKey="close" stroke={false} strokeWidth={2}
        fillOpacity={1} fill="url(#colorUv)" />
</ComposedChart>

Check the Area Chart example on CodePen.

Using SVG Drop Shadow Filter

With Recharts, you can apply SVG filters to the chart elements. The SVG <feDropShadow> creates a drop shadow effect of the input image. Tweaking dx, dy, stdDeviation, flood-color, and flood-opacity create different visual effects including glow, mirror, soft and hard shadow.

<LineChart {props}>
    <defs>
        <filter id="shadow" height="200%">
            <feDropShadow dx="0" dy="10" stdDeviation="10"/>
        </filter>
    </defs>
    ...
    <Line filter="url(#shadow)" {props} />
</LineChart>

Check the feDropShadow example on CodePen. Unfortunately, feDropShadow browser support is still not that great in 2020.

Combining Different SVG Filters

This approach is very similar to feDropShadow, except it uses widely supported filters to achieve similar effects. It gives more control over the process, thus more tweaking options to craft visually appealing charts. The following tricky filter blurs the image, offsets it, fills it with a slightly different color, and finally merges the results with the source.

<defs>
    <filter id="shadow" height="200%">
        <feGaussianBlur in="SourceAlpha" stdDeviation="7" result="blur" />
        <feOffset in="blur" dx="0" dy="7" result="offsetBlur" />
        <feFlood floodColor="#109065" floodOpacity="0.5" result="offsetColor" />
        <feComposite
        in="offsetColor"
        in2="offsetBlur"
        operator="in"
        result="offsetBlur"
        />
        <feMerge>
            <feMergeNode />
            <feMergeNode in="SourceGraphic" />
        </feMerge>
    </filter>
</defs>
...
<Line filter="url(#shadow)" {props} />

Tweaking the example on CodePen is fun and gives a better idea of how the filter works.

Adding Overlays Over SVG Charts

Displaying different data than the chart library supports often makes your charts much more informative. You can use HTML overlays to show anything. Recharts doesn’t have built-in support for this. Fortunately, they are very extendable, and it’s easy to add.

Using SVG foreignObject

ForeignObject allows you to include elements from a different XML namespace, usually (X)HTML, into the SVG. It’s pretty straightforward to use, and you don’t have to include the xmlns attribute for the XHTML namespace when inlining SVG into the HTML.

<foreignObject x="230" y="10" width="600" height="160">
  &nbsp;&nbsp;<h1 xmlns="http://www.w3.org/1999/xhtml">Hello from HTML</h1>
</foreignObject>

It’s supported in all major browsers, but the older browsers don’t understand it. You can use <switch> together with requiredExtensions attribute and provide a fallback for those cases.

<switch>
    <foreignObject width="100" height="50"
                    requiredExtensions="http://www.w3.org/TR/SVG11/feature#Extensibility">
        <!-- foreignObject is supported -->
    </foreignObject>

    <!-- Alternate SVG content if foreignObject is not supported -->
</switch>

Check out the example on CodePen.

Overlaying HTML With CSS

It’s incredibly straightforward. Just put your chart inside a container and position the overlay as you would typically do in HTML.

<div style={{position: "relative"}}>
    <ComposedChart {...props}/>

    <h1 style={{position: "absolute", top: 20, left: 220}}>Hello from HTML</h1>
</div>

We’ve also prepared a CodePen snippet.

Rendering SVG Elements

SVG is an abbreviation of Scalable Vector Graphics. It’s a very cool format and supports almost anything you could imagine, including raster images with an <image> element. You can conveniently render SVG with React, just like typical HTML markup. Mastering SVG takes some time. Fortunately, there are many great resources available to help you with that. We often look at “SVG element reference” from MDN for reference, but it’s also a great portal to learn.

<ComposedChart {...props}>
    ...
    <text x="220" y="40" dominantBaseline="hanging" fontSize="36" fontWeight="bold">
        Hello from SVG
    </text>
</ComposedChart>

Feel free to play around with SVG elements on our CodePen example.

Server-side rendering the ResponsiveContainer component

Google indexes SVG documents, and they might affect the SEO ranking of your web application. Recharts work great in isomorphic applications without any additional configuration, and their rendering on the server is incredibly fast. Unfortunately, their ResponsiveContainer component for creating responsive charts doesn’t work outside of the browser. The container has to know the dimensions before rendering, and there’s no way to know them for sure on the server during a page request—quite a legit reason for not implementing it. Server-side rendering is crucial for consumer applications, so we had to find a way to overcome it.

We can’t remember many examples where we’ve rendered charts with fixed dimensions. Those were either very small pie-charts or graphs on a prototype that wasn’t meant to work on smartphones yet. Modern web applications get more than half of the traffic from mobile devices, so they have to be responsive, and there are no other options.

Fortunately, there is a solution to make ResponsiveContainer work on the server. We have to look at its source code first. It’s not rendered because in renderChart() method which gets called inside the main render() there’s a check

const { containerWidth, containerHeight } = this.state;

if (containerWidth < 0 || containerHeight < 0) {
  return null;
}

Those are first initialized inside the constructor with -1 and got updated inside ReactResizeDetector onResize callback. Well, that won’t work on a server.

Quite an easy fix would be to add optional Props initialWidth and initialHeight. Then initialize containerWidth and containerHeight with those values inside the constructor. There’s no need to react on updates of those properties as they are used only for the first render. Now the charts would render with passed dimensions on the server and pick up the correct window dimensions during the next render after hydration on the client.

It creates a weird effect when the initial size of the charts is way off on some platforms. Keep in mind that React hydration on the client should produce the same output as the HTML from the server-side rendering, or it would have trouble mapping DOM to components. We have to pass the same initialWidth and initialHeight both on the client and on the server.

One option would be to keep everything as is, especially if it’s hidden by the scroll. Better, while more time consuming, would be to guess the browser’s dimension on the server using UserAgent, for example. Many libraries do that, and you have to map the device with potential chart size and use that when rendering. The same logic should work on the client. Still, in case you’ll get into trouble with slightly different device detection, you can always save the calculated dimensions somewhere in the rendered HTML document and restore it on the client before hydration.

You will have to use your version of ResponsiveContainer, and submitting them a pull request would be awesome. We are using our custom component for that, it’s a bit more sophisticated, although built on the same principles. The same approach works great for mobile optimizations for components that are visible only on bigger screens and have complex rendering logic or make additional API calls to the backend. You can even swap the different implementations entirely if the mobile version is completely different from desktop.

Conclusion

Charts are an efficient instrument for condensing complex information into a compact human-readable form. Tweaking them requires a lot of effort, especially when done for the first time. Depending on the design, they can either make or break the whole page. It’s always well worth to spend extra time and make them visually appealing and in style with the rest of the application.

Designing and building excellent React charts takes mastery and experience. It’s a broad topic, and we only covered a small fraction of it in this article.

Don’t hesitate to book a free consultation if you need a bit of advice on how to improve your charts. We’re always happy to help.