HTVG is a static compiler that converts JSON element trees to pure SVG with correct multiline text layout and flexbox support. This guide will get you from zero to working SVG output in just a few minutes.
Install HTVG
Choose your preferred environment:
Initialize (JavaScript only)
If you’re using JavaScript, you need to initialize the WASM module before compilation: Browser
Node.js
Cloudflare Workers
import { init , compileDocument } from 'htvg' ;
// Auto-fetches the .wasm file
await init ();
Rust users can skip this step - no initialization required!
Create your first SVG
Let’s create a simple “Hello World” document: JavaScript
Rust
JSON File
import { init , compileDocument } from 'htvg' ;
await init ();
const result = compileDocument ({
meta: { width: 400 },
content: {
type: "flex" ,
style: {
width: 400 ,
padding: 32 ,
backgroundColor: "#ffffff" ,
flexDirection: "column" ,
gap: 12
},
children: [
{
type: "text" ,
content: "Hello, HTVG!" ,
style: {
fontSize: 32 ,
fontWeight: "bold" ,
color: "#1a1a1a"
}
},
{
type: "text" ,
content: "This is a self-contained document rendered to SVG." ,
style: {
fontSize: 16 ,
color: "#666666"
}
}
]
}
});
console . log ( result . svg ); // SVG string
console . log ( result . width ); // 400
console . log ( result . height ); // auto-computed
use htvg :: {compile, CompileOptions };
fn main () -> Result <(), Box < dyn std :: error :: Error >> {
let json = r#"{
"type": "flex",
"style": {
"width": 400,
"padding": 32,
"backgroundColor": "# ffffff ",
" flexDirection ": " column ",
" gap ": 12
},
" children ": [
{
" type ": " text ",
" content ": " Hello , HTVG ! ",
" style ": {
" fontSize ": 32,
" fontWeight ": " bold ",
" color ": " # 1a1a1a "
}
},
{
" type ": " text ",
" content ": " This is a self - contained document rendered to SVG . ",
" style ": {
" fontSize ": 16,
" color ": " # 666666 "
}
}
]
}" #;
let options = CompileOptions {
width : 400.0 ,
height : None , // Auto-computed
font_size : 16.0 ,
.. CompileOptions :: default ()
};
let result = compile ( json , & options ) ? ;
println! ( "{}" , result . svg);
Ok (())
}
Create a file called hello.json: {
"meta" : {
"width" : 400
},
"content" : {
"type" : "flex" ,
"style" : {
"width" : 400 ,
"padding" : 32 ,
"backgroundColor" : "#ffffff" ,
"flexDirection" : "column" ,
"gap" : 12
},
"children" : [
{
"type" : "text" ,
"content" : "Hello, HTVG!" ,
"style" : {
"fontSize" : 32 ,
"fontWeight" : "bold" ,
"color" : "#1a1a1a"
}
},
{
"type" : "text" ,
"content" : "This is a self-contained document rendered to SVG." ,
"style" : {
"fontSize" : 16 ,
"color" : "#666666"
}
}
]
}
}
Then compile it with the JavaScript API: import { init , compileDocument } from 'htvg' ;
import fs from 'node:fs' ;
await init ( fs . readFileSync ( 'node_modules/htvg/dist/wasm/htvg_bg.wasm' ));
const doc = JSON . parse ( fs . readFileSync ( 'hello.json' , 'utf-8' ));
const result = compileDocument ( doc );
fs . writeFileSync ( 'hello.svg' , result . svg );
Build something more complex
Let’s create a card component with borders, rounded corners, and nested layouts: {
"meta" : {
"width" : 360
},
"content" : {
"type" : "flex" ,
"style" : {
"width" : 360 ,
"padding" : 24 ,
"backgroundColor" : "#f8f9fa" ,
"flexDirection" : "column" ,
"gap" : 16
},
"children" : [
{
"type" : "flex" ,
"style" : {
"backgroundColor" : "#ffffff" ,
"borderRadius" : 12 ,
"borderWidth" : 1 ,
"borderColor" : "#e0e0e0" ,
"padding" : 20 ,
"flexDirection" : "column" ,
"gap" : 8
},
"children" : [
{
"type" : "text" ,
"content" : "Card Title" ,
"style" : {
"fontSize" : 20 ,
"fontWeight" : 700 ,
"color" : "#1a1a1a"
}
},
{
"type" : "text" ,
"content" : "This is a card component with a border, rounded corners, and some padding. It demonstrates nested flex containers." ,
"style" : {
"fontSize" : 14 ,
"color" : "#555555" ,
"lineHeight" : 1.5
}
},
{
"type" : "flex" ,
"style" : {
"flexDirection" : "row" ,
"gap" : 8 ,
"padding" : "12 0 0 0"
},
"children" : [
{
"type" : "box" ,
"style" : {
"width" : 80 ,
"height" : 32 ,
"backgroundColor" : "#2563eb" ,
"borderRadius" : 6
}
},
{
"type" : "box" ,
"style" : {
"width" : 80 ,
"height" : 32 ,
"backgroundColor" : "#e2e8f0" ,
"borderRadius" : 6
}
}
]
}
]
}
]
}
}
This example demonstrates:
Nested flex containers
Border and border-radius styling
Multi-value padding ("padding": "12 0 0 0")
Row and column layouts
Proper text wrapping with lineHeight
Use the two compilation modes
HTVG provides two ways to compile your documents: Best for: Self-contained documents with metadataconst result = compileDocument ({
meta: {
width: 600 ,
fonts: [
{ family: "Inter" , url: "https://fonts.gstatic.com/..." }
]
},
content: {
type: "text" ,
content: "Hello" ,
style: { fontSize: 32 , fontFamily: "Inter" }
}
});
Best for: Separating element structure from optionsimport { init , compile } from 'htvg' ;
await init ();
const element = {
type: "flex" ,
style: { flexDirection: "column" , gap: 8 },
children: [
{
type: "text" ,
content: "Title" ,
style: { fontSize: 20 , fontWeight: 700 }
}
]
};
const result = compile ( element , { width: 400 });
Next Steps
Element Types Learn about all available element types: box, flex, text, and image
Style Properties Explore layout, flex, visual, and typography properties
API Reference Complete API documentation for JavaScript and Rust
Examples Browse real-world examples and templates
Common Patterns
Creating badges
{
"type" : "flex" ,
"style" : {
"backgroundColor" : "#22c55e" ,
"borderRadius" : 16 ,
"padding" : "6 14"
},
"children" : [
{
"type" : "text" ,
"content" : "Success" ,
"style" : { "fontSize" : 13 , "fontWeight" : 600 , "color" : "#ffffff" }
}
]
}
Responsive width
Use percentage strings for fluid layouts:
{
"type" : "box" ,
"style" : {
"width" : "100%" ,
"padding" : 20
}
}
Custom fonts
const result = compileDocument ({
meta: {
width: 400 ,
fonts: [
{ family: "Geist" , url: "https://example.com/geist.woff2" }
]
},
content: {
type: "text" ,
content: "Custom font text" ,
style: { fontFamily: "Geist" , fontSize: 18 }
}
});
The height is automatically computed based on content unless explicitly specified in meta.height.