Custom Charts
Victory lets you create fully custom charts that integrate seamlessly with the look and feel of your project. The following guide demonstrates how custom styles and modular chart components are used to create a cohesive chart with distinctive branding.
The following example shows how to create a chart with multiple independent axes without using the VictoryChart
wrapper. This example also includes functional styles and axis customization.
function CustomTheme() { const styles = getStyles(); const dataSetOne = getDataSetOne(); const dataSetTwo = getDataSetTwo(); const tickValues = getTickValues(); return ( <svg style={styles.parent} viewBox="0 0 450 350"> {/* Create stylistic elements */} <rect x="0" y="0" width="10" height="30" fill="#f01616"/> <rect x="420" y="10" width="20" height="20" fill="#458ca8"/> {/* Define labels */} <VictoryLabel x={25} y={24} style={styles.title} text="An outlook" /> <VictoryLabel x={430} y={20} style={styles.labelNumber} text="1" /> <VictoryLabel x={25} y={55} style={styles.labelOne} text={"Economy \n % change on a year earlier"} /> <VictoryLabel x={425} y={55} style={styles.labelTwo} text={"Dinosaur exports\n $bn"} /> <g transform={"translate(0, 40)"}> {/* Add shared independent axis */} <VictoryAxis scale="time" standalone={false} style={styles.axisYears} tickValues={tickValues} tickFormat={ (x) => { if (x.getFullYear() === 2000) { return x.getFullYear(); } if (x.getFullYear() % 5 === 0) { return x.getFullYear().toString().slice(2); } } } /> {/* Add the dependent axis for the first data set. Note that all components plotted against this axis will have the same y domain */} <VictoryAxis dependentAxis domain={[-10, 15]} offsetX={50} orientation="left" standalone={false} style={styles.axisOne} /> {/* Red annotation line */} <VictoryLine data={[ {x: new Date(1999, 1, 1), y: 0}, {x: new Date(2014, 6, 1), y: 0} ]} domain={{ x: [new Date(1999, 1, 1), new Date(2016, 1, 1)], y: [-10, 15] }} scale={{x: "time", y: "linear"}} standalone={false} style={styles.lineThree} /> {/* dataset one */} <VictoryLine data={dataSetOne} domain={{ x: [new Date(1999, 1, 1), new Date(2016, 1, 1)], y: [-10, 15] }} interpolation="monotoneX" scale={{x: "time", y: "linear"}} standalone={false} style={styles.lineOne} /> {/* Add the dependent axis for the second data set. Note that all components plotted against this axis will have the same y domain */} <VictoryAxis dependentAxis domain={[0, 50]} orientation="right" standalone={false} style={styles.axisTwo} /> {/* dataset two */} <VictoryLine data={dataSetTwo} domain={{ x: [new Date(1999, 1, 1), new Date(2016, 1, 1)], y: [0, 50] }} interpolation="monotoneX" scale={{x: "time", y: "linear"}} standalone={false} style={styles.lineTwo} /> </g> </svg> ); } function getDataSetOne() { return [ {x: new Date(2000, 1, 1), y: 12}, {x: new Date(2000, 6, 1), y: 10}, {x: new Date(2000, 12, 1), y: 11}, {x: new Date(2001, 1, 1), y: 5}, {x: new Date(2002, 1, 1), y: 4}, {x: new Date(2003, 1, 1), y: 6}, {x: new Date(2004, 1, 1), y: 5}, {x: new Date(2005, 1, 1), y: 7}, {x: new Date(2006, 1, 1), y: 8}, {x: new Date(2007, 1, 1), y: 9}, {x: new Date(2008, 1, 1), y: -8.5}, {x: new Date(2009, 1, 1), y: -9}, {x: new Date(2010, 1, 1), y: 5}, {x: new Date(2013, 1, 1), y: 1}, {x: new Date(2014, 1, 1), y: 2}, {x: new Date(2015, 1, 1), y: -5} ]; } function getDataSetTwo() { return [ {x: new Date(2000, 1, 1), y: 5}, {x: new Date(2003, 1, 1), y: 6}, {x: new Date(2004, 1, 1), y: 4}, {x: new Date(2005, 1, 1), y: 10}, {x: new Date(2006, 1, 1), y: 12}, {x: new Date(2007, 2, 1), y: 48}, {x: new Date(2008, 1, 1), y: 19}, {x: new Date(2009, 1, 1), y: 31}, {x: new Date(2011, 1, 1), y: 49}, {x: new Date(2014, 1, 1), y: 40}, {x: new Date(2015, 1, 1), y: 21} ]; } function getTickValues() { return [ new Date(1999, 1, 1), new Date(2000, 1, 1), new Date(2001, 1, 1), new Date(2002, 1, 1), new Date(2003, 1, 1), new Date(2004, 1, 1), new Date(2005, 1, 1), new Date(2006, 1, 1), new Date(2007, 1, 1), new Date(2008, 1, 1), new Date(2009, 1, 1), new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1), new Date(2014, 1, 1), new Date(2015, 1, 1), new Date(2016, 1, 1) ]; } function getStyles() { const BLUE_COLOR = "#00a3de"; const RED_COLOR = "#7c270b"; return { parent: { background: "#ccdee8", boxSizing: "border-box", display: "inline", padding: 0, fontFamily: "'Fira Sans', sans-serif" }, title: { textAnchor: "start", verticalAnchor: "end", fill: "#000000", fontFamily: "inherit", fontSize: "18px", fontWeight: "bold" }, labelNumber: { textAnchor: "middle", fill: "#ffffff", fontFamily: "inherit", fontSize: "14px" }, // INDEPENDENT AXIS axisYears: { axis: { stroke: "black", strokeWidth: 1}, ticks: { size: ({ tick }) => { const tickSize = tick.getFullYear() % 5 === 0 ? 10 : 5; return tickSize; }, stroke: "black", strokeWidth: 1 }, tickLabels: { fill: "black", fontFamily: "inherit", fontSize: 16 } }, // DATA SET ONE axisOne: { grid: { stroke: ({ tick }) => tick === -10 ? "transparent" : "#ffffff", strokeWidth: 2 }, axis: { stroke: BLUE_COLOR, strokeWidth: 0 }, ticks: { strokeWidth: 0 }, tickLabels: { fill: BLUE_COLOR, fontFamily: "inherit", fontSize: 16 } }, labelOne: { fill: BLUE_COLOR, fontFamily: "inherit", fontSize: 12, fontStyle: "italic" }, lineOne: { data: { stroke: BLUE_COLOR, strokeWidth: 4.5 } }, axisOneCustomLabel: { fill: BLUE_COLOR, fontFamily: "inherit", fontWeight: 300, fontSize: 21 }, // DATA SET TWO axisTwo: { axis: { stroke: RED_COLOR, strokeWidth: 0 }, tickLabels: { fill: RED_COLOR, fontFamily: "inherit", fontSize: 16 } }, labelTwo: { textAnchor: "end", fill: RED_COLOR, fontFamily: "inherit", fontSize: 12, fontStyle: "italic" }, lineTwo: { data: { stroke: RED_COLOR, strokeWidth: 4.5 } }, // HORIZONTAL LINE lineThree: { data: { stroke: "#e95f46", strokeWidth: 2 } } }; } render(<CustomTheme/>);