Integrate Charts and Conditional Rendering
- How to install and import charts
- Learn about charts in UI5 Web Components for React
- How to add dynamic rendering
UI5 Web Components for React also comes with a chart library. In this tutorial, you will integrate two chart types and add data to them. Also, you will learn how to conditionally render components, and how React handles updates to the DOM and single components.
- Step 1
-
Install the chart library of UI5 Web Components for React.
ShellCopynpm install @ui5/webcomponents-react-chartsNote:
When facing issues with installing, please ensure you’re using the same minor version as the other@ui5/webcomponents...packages. -
Then, import
LineChartandBarChartintoMyApp.tsx.TypeScript / TSXCopyimport { BarChart, LineChart } from "@ui5/webcomponents-react-charts";
-
- Step 2
Charts only offer limited accessibility support with only basic built-in features, so it’s essential to ensure your implementation meets the accessibility standards of your application!
-
Start with the
LineChart. You can add it underneath theTextcomponent. Then pass thedimensionsandmeasuresprop with an empty array as value.TypeScript / TSXCopy<Text style={{ padding: "var(--sapContent_Space_S)" }}> This is the content area of the Card </Text> <LineChart measures={[]} dimensions={[]} />Now you should see a placeholder for the
LineChartwithin theCardcontent. The reason for this is that the chart has not received any data and therefore the placeholder is displayed. -
Add data to your component. Since
datais static you can define it outside the component, right above yourMyAppcomponent.TypeScript / TSXCopyconst dataset = [ { month: "January", data: 65 }, { month: "February", data: 59 }, { month: "March", data: 80 }, { month: "April", data: 81 }, { month: "May", data: 56 }, { month: "June", data: 55 }, { month: "July", data: 40 } ]; -
Now add the
datasetto yourLineChartand configure thedimensionsandmeasuresprops.TypeScript / TSXCopy<LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} />Congratulation, you implemented your first Chart component.

-
Add a
BarChartto theCard.We want the same data just with a different representation, therefore you can use the same props as you did with the
LineChart.TypeScript / TSXCopy<BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} />Two charts are rendered now with equal datasets but different representation.
Your
MyApp.tsxcomponent should look like this:TypeScript / TSXCopyimport { Card, CardHeader, Text } from "@ui5/webcomponents-react"; import { BarChart, LineChart } from "@ui5/webcomponents-react-charts"; const dataset = [ { month: "January", data: 65, }, { month: "February", data: 59, }, { month: "March", data: 80, }, { month: "April", data: 81, }, { month: "May", data: 56, }, { month: "June", data: 55, }, { month: "July", data: 40, }, ]; export function MyApp() { const handleHeaderClick = () => { alert("Header clicked"); }; return ( <div> <Card header={ <CardHeader titleText="Card" interactive onClick={handleHeaderClick} /> } style={{ width: "300px" }} > <Text style={{ padding: "var(--sapContent_Space_S)" }}> This is the content area of the Card </Text> <LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} /> <BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} /> </Card> </div> ); } -
- Step 3
Two charts in one
Cardis a bit too much, don’t you think? It would be nicer if the charts could be toggled by clicking on the header. Let’s implement that!-
First add a state. It should control, which chart is going to be rendered. Use the State Hook logic to implement the state and set
"lineChart"as default value. Don’t forget to importuseStatefrom React, otherwise you will get an error.-
Import the
useStatefunction in the header of theMyApp.tsxfile.TypeScript / TSXCopyimport { useState } from "react"; -
Use the
useStatefunction in the right after you start to define theMyAppfunction (before the click handler).TypeScript / TSXCopyconst [toggleCharts, setToggleCharts] = useState("lineChart");
-
-
By clicking on the
CardHeaderthe state should be set corresponding to the chart which should be displayed.Rewrite your
onClickfunction so it will handle this logic.TypeScript / TSXCopyconst handleHeaderClick = () => { if (toggleCharts === "lineChart") { setToggleCharts("barChart"); } else { setToggleCharts("lineChart"); } }; -
To only render the current chart, add the following lines to the render of the component:
TypeScript / TSXCopy<Card header={ <CardHeader titleText="Card" interactive onClick={handleHeaderClick} /> } style={{ width: "300px" }} > <Text style={spacing.sapUiContentPadding}> This is the content area of the Card </Text> {toggleCharts === "lineChart" ? ( <LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} /> ) : ( <BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} /> )} </Card>Done! Now you can toggle between charts by clicking on the header of the
Card. -
You can further improve your
CardHeadercomponent by using theavatarpropand adding anIconto it.Add the following import to your component:
TypeScript / TSXCopyimport { Card, CardHeader, Text, Icon } from "@ui5/webcomponents-react";Icons can be imported altogether (
import '@ui5/webcomponents-icons/dist/AllIcons.js';), but to reduce bundle size and for better maintainability, it’s recommended importing each icon on its own:TypeScript / TSXCopyimport lineChartIcon from '@ui5/webcomponents-icons/dist/line-chart.js'; import barChartIcon from '@ui5/webcomponents-icons/dist/horizontal-bar-chart.js';The
Iconsshould also be conditionally rendered. Luckily, this is easy:Add the
avatarprop to theCardHeader, which receives anIconas value:TypeScript / TSXCopy<CardHeader ... avatar={<Icon name={lineChartIcon} />} />Then, change the
nameprop of theIconto the following:TypeScript / TSXCopy<CardHeader avatar={ <Icon name={ toggleCharts === "lineChart" ? lineChartIcon : barChartIcon } /> } ... />Here we go! Now the
Cardalso changes theIconby clicking on the header.
If something went wrong you can compare your component to this code snippet:
TypeScript / TSXCopyimport lineChartIcon from "@ui5/webcomponents-icons/dist/line-chart.js"; import barChartIcon from "@ui5/webcomponents-icons/dist/horizontal-bar-chart.js"; import { useState } from "react"; import { Card, CardHeader, Text, Icon } from "@ui5/webcomponents-react"; import { BarChart, LineChart } from "@ui5/webcomponents-react-charts"; const dataset = [ { month: "January", data: 65, }, { month: "February", data: 59, }, { month: "March", data: 80, }, { month: "April", data: 81, }, { month: "May", data: 56, }, { month: "June", data: 55, }, { month: "July", data: 40, }, ]; export function MyApp() { const [toggleCharts, setToggleCharts] = useState("lineChart"); const [loading, setLoading] = useState(false); const handleHeaderClick = () => { if (toggleCharts === "lineChart") { setLoading(true); setTimeout(() => { setLoading(false); setToggleCharts("barChart"); }, 2000); } else { setLoading(true); setTimeout(() => { setLoading(false); setToggleCharts("lineChart"); }, 2000); } }; return ( <div> <Card header={ <CardHeader titleText="Card" interactive avatar={ <Icon name={ toggleCharts === "lineChart" ? lineChartIcon : barChartIcon } /> } onClick={handleHeaderClick} /> } style={{ width: "300px" }} > <Text style={{ padding: "var(--sapContent_Space_S)" }}> This is the content area of the Card </Text> {toggleCharts === "lineChart" ? ( <LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} /> ) : ( <BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} /> )} </Card> </div> ); }Would it be possible to switch between more than two charts by adding some more code?
-
- Step 4
One of React’s main advantages is its efficient rendering mechanism: a component re-renders only when its state changes or when it receives new props from its parent. So it will not update the whole UI, but only components affected by changes.
-
In order to demonstrate this behavior, add a new
state(right after the definition of the previous state).TypeScript / TSXCopyconst [loading, setLoading] = useState(false); -
Then edit your
handleHeaderClickfunction like this:TypeScript / TSXCopyconst handleHeaderClick = () => { if (toggleCharts === "lineChart") { setLoading(true); setTimeout(() => { setLoading(false); setToggleCharts("barChart"); }, 2000); } else { setLoading(true); setTimeout(() => { setLoading(false); setToggleCharts("lineChart"); }, 2000); } }; -
Add
loadingto both of your charts.TypeScript / TSXCopy<LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} loading={loading} />TypeScript / TSXCopy<BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} loading={loading} />
This updates the component every time you switch between charts and simulates a data call.
As you can see, only the component affected by the
stateis updated, and the rest stays the same. If you’re working with data, you most probably will need a loading indicator. All UI5 web components that are able to display data have aloadingprop and therefore also a loading indicator.If a prop of a component changes, will the component be updated?
-
- Step 5
To make your
Cardlook cleaner and to give the user the information that the header is clickable, you can add some more logic to your component.-
Add a dynamic content
TextThe content text is not really informative. Let’s change that and display the type of the chart. Add the following constants to your component (e.g. after the state definitions):
TypeScript / TSXCopyconst contentTitle = toggleCharts === 'lineChart' ? 'Line Chart' : 'Bar Chart'; const switchToChart = toggleCharts === 'lineChart' ? 'Bar Chart' : 'Line Chart'; -
Change the title and add a subtitle to your
CardFirst change the value of
titleTextto something that explains the content of theCard(e.g.,"Prices").
Then add asubtitleTextprop. Here you can give the users the information that they can switch between charts by clicking the header.TypeScript / TSXCopy<Card header={ <CardHeader titleText="Prices" subtitleText={`Click here to switch to ${switchToChart}`} interactive avatar={ <Icon name={ toggleCharts === "lineChart" ? lineChartIcon : barChartIcon } /> } onClick={handleHeaderClick} /> } style={{ width: "300px" }} > <Text style={{ padding: "var(--sapContent_Space_S)" }}> {contentTitle} </Text> {toggleCharts === "lineChart" ? ( <LineChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} loading={loading} /> ) : ( <BarChart dimensions={[{ accessor: "month" }]} measures={[{ accessor: "data", label: "Price" }]} dataset={dataset} loading={loading} /> )} </Card> - Add accessibility attributes to the
IconIf the
headeralone does not sufficiently describe the currently visibleIcon, you can define anaccessibleName. Its value will be applied as thearia-labelof the internal element, which screen readers will pick up.
In case theheaderalready explains the content and the icon is purely decorative, you can set itsmodeto"Decorative", so it will be ignored by screen readers.<Icon name={ toggleCharts === "lineChart" ? lineChartIcon : barChartIcon } accessibleName={contentTitle} />
-