JavaFX is packaged with several default ways to include vector graphics images. SVGs can be loaded into a JavaFX application by setting a Node’s shape using CSS, or by creating an SVGPath.
What we’ll have by the end of the tutorial
We’re going to load SVG images and paths into JavaFX in three different ways:
In each of these cases, the SVG we load in is going to be a node, just like any other UI element, which means you you’ll probably end up adding it to a layout just like anything else. If you haven’t checked out my article on layouts, I would definitely recommend it.
In it, I tried to create a resource that explains every layout and how it sizes and positions it’s children, which is especially important for handling images when you load them into a program. And, honestly, the time I’ve saved just referring back to it myself while I’m designing programs made it time well-spent.
Loading an SVG into JavaFX
There are pros and cons to each way of loading the data into your program, but it more or less boils down to this:
Are you willing to maintain an additional dependency for the convenience of not having to soft through your SVG files?
Convert to Buffered image
Load with CSS
Load with SVGPath
We’ll start with the conversion method, because this doesn’t require any knowledge of the SVG format. We’ll then take a look through the CSS and SVGPath approaches. And don’t worry we’ll take you through how to get the data out of the SVG file too.
Converting an SVG file to a BufferedImage
Before we do this it’s important to remember that because of a bug in one of the Batik dependencies, this won’t work on a module-path project, and you’ll therefore need to set your JavaFX project to run from the classpath as in this tutorial here.
If you need support for all the functionality of an SVG file, the final and most comprehensive way to incorporate an image into your program is to use an image transcoder. One of the main benefits of Java is its comprehensive set of well-maintained libraries and the one we’ll use today is the Batik Transcoder from Apache, which you can include as a dependency if you’re managing your project with Maven.
<dependency> <groupId>org.apache.xmlgraphics</groupId> <artifactId>batik-transcoder</artifactId> <version>1.13</version> </dependency>
If your project doesn’t have Maven support, you can download the Jar from Maven Central here.
Either way, we won’t use Maven for importing JavaFX, because of the Batik bug, so it’s entirely up to you. Next, we need to extend the Batik’s ImageTranscoder
class – it’s abstract, so we just need to instantiate it and store the image we create.
public class BufferedImageTranscoder extends ImageTranscoder { private BufferedImage img = null; @Override public BufferedImage createImage(int width, int height) { return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); } @Override public void writeImage(BufferedImage img, TranscoderOutput to) throws TranscoderException { this.img = img; } public BufferedImage getBufferedImage() { return img; } }
Next, we need to add a dependency for JavaFX’s Swing utilities, because we’ll have to convert the SVG into a BufferedImage using AWT.
import javafx.embed.swing.SwingFXUtils;
That’s important, because the Batik transcoder only translates our SVG file into a java.awt.image.BufferedImage
and not a JavaFX BufferedImage. To use it, we need to use the SwingFXUtils
static utility class to get the right sort of image for JavaFX.
The following code is wrapped in two try-catch blocks. The first, a try-with-resources, opens the SVG file as an InputStream. The second converts the image into a BufferedImage and saves it to an ImageView
.
ImageView githubImage = new ImageView(); BufferedImageTranscoder transcoder = new BufferedImageTranscoder(); try (InputStream file = getClass().getResourceAsStream("/img/github.svg")) { TranscoderInput transIn = new TranscoderInput(file); try { transcoder.transcode(transIn, null); Image img = SwingFXUtils.toFXImage(trans.getBufferedImage(), null); githubImage.setImage(img); } catch (TranscoderException ex) { ex.printStackTrace(); } } catch (IOException io) { io.printStackTrace(); }
Finally, we set the image size, and add it to our scene. This time, the transcoder takes the color from the SVG metadata, so we aren’t free to set it programatically.
githubImage.setFitHeight(25); githubImage.setPreserveRatio(true); contentContainer.getChildren().add(githubImage);
Result!

Loading an SVG with CSS
To start, we’ll take a quick look at the SVG format, because to use some of the options below, you’ll need to take the important information out of the SVG file, so it’s important to know where to get it.
Click the drop-down to learn more about the SVG file. Or, if you’re comfortable getting path data from an SVG, we can dive right in.
SVG Format
The SVG file has a pretty complicated specification – it’s an extensible markup that has – on top of the stuff you’ll use regularly – support for simple geometric shapes and lines. What we’ll need – and what the overwhelming majority of applications use – is paths. In an SVG file, they’re defined with the <path>
tag.
Let’s start with a popular image – the GitHub logo – and let’s make it a part of our program.
Unlike most image files, you can open an SVG up straight into a text editor. The whole basis of an SVG file is markup, so we just have to look for the path element.
Path elements
<svg enable-background="new 0 0 24 24" height="128" viewBox="0 0 24 24" width="128" xmlns="http://www.w3.org/2000/svg"> <path d="m12 .5c-6.63 0-12 5.28-12 11.792 0 5.211 3.438 9.63 8.205 11.188.6.111.82-.254.82-.567 0-.28-.01-1.022-.015-2.005-3.338.711-4.042-1.582-4.042-1.582-.546-1.361-1.335-1.725-1.335-1.725-1.087-.731.084-.716.084-.716 1.205.082 1.838 1.215 1.838 1.215 1.07 1.803 2.809 1.282 3.495.981.108-.763.417-1.282.76-1.577-2.665-.295-5.466-1.309-5.466-5.827 0-1.287.465-2.339 1.235-3.164-.135-.298-.54-1.497.105-3.121 0 0 1.005-.316 3.3 1.209.96-.262 1.98-.392 3-.398 1.02.006 2.04.136 3 .398 2.28-1.525 3.285-1.209 3.285-1.209.645 1.624.24 2.823.12 3.121.765.825 1.23 1.877 1.23 3.164 0 4.53-2.805 5.527-5.475 5.817.42.354.81 1.077.81 2.182 0 1.578-.015 2.846-.015 3.229 0 .309.21.678.825.56 4.801-1.548 8.236-5.97 8.236-11.173 0-6.512-5.373-11.792-12-11.792z" fill="#212121"/> </svg>
The path data is halfway down the file beneath some meta data, which tells the computer the default way the image should be rendered. Pixel Perfect provided this SVG, which you can find at flaticon.com, because you’re probably going to use stock SVGs a lot. The path data is just that one highlighted line of code.
Decoding the SVG
If you’re interested in what it means, you can break down a <path>
into steps. Each letter is an instruction, and they have a defined meaning: ‘M’ instructs the path to move to the location specified without drawing a line. In this case, the first three instructions are:
Instruction | Meaning |
---|---|
m12 .5 | Move to (12, 0.5) |
c-6.63 0-12 5.28-12 11.792 | Draw a Bezier curve to (-12, 11.792) Control points: (-6.63, 0) (12, 5.28) |
0 5.211 3.438 9.63 8.205 11.188 | Draw a Bezier curve to (8.205 11.188) Control points: (0 5.211) (3.438 9.63) |
Instruction 3 doesn’t contain a “c” command to draw the Bezier curve, because Instruction 2 contained the same instruction. If a set of numbers doesn’t have a command descriptor, infer it from the descriptor used before.
The simplest way to add an SVG path into a program is to define it in the CSS layer, by using the -fx-shape
attribute. Any node can have an -fx-shape
attribute. If you’re applying it to a button, the button will take that shape. If you’re applying it to a Region, it redefines the shape of that whole region!
As a starting point, we’ll use the simple UI we designed in Choosing a UI for Style. The simple UI consists at the moment of just a logo and an exit button.

To add a button with CSS, we’ll add the button directly into the FXML document, MainView.fxml
:
<Button styleClass="git-button"/>
…and we’ll add the style class we just referenced into application.css
:
.git-button { -fill: -blue; -fx-shape: "m12 .5c-6.63 0-12 5.28-12 11.792 0 5.211 3.438 9.63 8.205 11.188.6.111.82-.254.82-.567 0-.28-.01-1.022-.015-2.005-3.338.711-4.042-1.582-4.042-1.582-.546-1.361-1.335-1.725-1.335-1.725-1.087-.731.084-.716.084-.716 1.205.082 1.838 1.215 1.838 1.215 1.07 1.803 2.809 1.282 3.495.981.108-.763.417-1.282.76-1.577-2.665-.295-5.466-1.309-5.466-5.827 0-1.287.465-2.339 1.235-3.164-.135-.298-.54-1.497.105-3.121 0 0 1.005-.316 3.3 1.209.96-.262 1.98-.392 3-.398 1.02.006 2.04.136 3 .398 2.28-1.525 3.285-1.209 3.285-1.209.645 1.624.24 2.823.12 3.121.765.825 1.23 1.877 1.23 3.164 0 4.53-2.805 5.527-5.475 5.817.42.354.81 1.077.81 2.182 0 1.578-.015 2.846-.015 3.229 0 .309.21.678.825.56 4.801-1.548 8.236-5.97 8.236-11.173 0-6.512-5.373-11.792-12-11.792z"; }
It could not be easier!

If you’re interested, you can make the button launch the default internet browser with just a few lines of code. Once you’ve added onAction=”handleGitButtonClicked” to the FXML button defintion, it’s as simple as adding this to your Controller:
@FXML private void handleGitButtonClicked(ActionEvent event) { new Application() { @Override public void start(Stage stage) { } }.getHostServices().showDocument("https://github.com/edencoding/javafx-ui/"); event.consume(); }
Using an SVGPath as an image
If you need to load an SVG in programmatically, JavaFX provides functionality to handle the <path>
part of an SVG using its niftily-named SVGPath
class. That additional functionality of regular shapes and other extensible functionality is not supported, but we can still load the SVG path into our program with a little code.
SVGPath githubIcon = new SVGPath(); githubIcon.setContent("m12 .5c-6.63 0-12 5.28-12 11.792 0 5.211 3.438 9.63 8.205 11.188.6.111.82-.254.82-.567 0-.28-.01-1.022-.015-2.005-3.338.711-4.042-1.582-4.042-1.582-.546-1.361-1.335-1.725-1.335-1.725-1.087-.731.084-.716.084-.716 1.205.082 1.838 1.215 1.838 1.215 1.07 1.803 2.809 1.282 3.495.981.108-.763.417-1.282.76-1.577-2.665-.295-5.466-1.309-5.466-5.827 0-1.287.465-2.339 1.235-3.164-.135-.298-.54-1.497.105-3.121 0 0 1.005-.316 3.3 1.209.96-.262 1.98-.392 3-.398 1.02.006 2.04.136 3 .398 2.28-1.525 3.285-1.209 3.285-1.209.645 1.624.24 2.823.12 3.121.765.825 1.23 1.877 1.23 3.164 0 4.53-2.805 5.527-5.475 5.817.42.354.81 1.077.81 2.182 0 1.578-.015 2.846-.015 3.229 0 .309.21.678.825.56 4.801-1.548 8.236-5.97 8.236-11.173 0-6.512-5.373-11.792-12-11.792z"); githubIcon.setFill(Color.web("#81c483")); contentContainer.getChildren().add(githubIcon);

In this case we’ve added it manually into the scene as an image, and manually set the color in the third line of code. You can also scale the images with setScaleX()
and setScaleY()
methods of the SVGPath
class for convenience.
Conclusions
JavaFX bundles two ways to load SVG images into your application. You can set the shape of any node – usually a button – by setting the -fx-shape
attribute via CSS.
You can also load an SVG’s path using JavaFX’s SVGPath class, programmatically setting extra values such as color and size in your program rather than in CSS.
Finally, if you need to load an SVG in with additional support for functionality other than the <path>
tag, you can use a library transcoder to convert it into a BufferedImage
and load that into your application. A word of caution, though, as once transcoded, you’ll lose the ability to scale without loss of quality.