React Native Skia provides powerful text rendering capabilities with support for custom fonts, text paths, glyphs, and paragraphs.
Text Component
Renders text at a specific position.
import { Canvas, Text, matchFont } from "@shopify/react-native-skia";
const font = matchFont({
fontFamily: "Helvetica",
fontSize: 24,
fontWeight: "bold",
});
<Canvas style={{ flex: 1 }}>
<Text x={50} y={100} text="Hello, Skia!" font={font} color="black" />
</Canvas>
Props
The text string to render
X-coordinate of the text baseline start
Y-coordinate of the text baseline
Font object created with matchFont() or Skia.Font()
Creating Fonts
Using matchFont
Match system fonts:
import { matchFont } from "@shopify/react-native-skia";
const regularFont = matchFont({
fontFamily: "Arial",
fontSize: 16,
});
const boldFont = matchFont({
fontFamily: "Arial",
fontSize: 16,
fontWeight: "bold",
});
const italicFont = matchFont({
fontFamily: "Arial",
fontSize: 16,
fontStyle: "italic",
});
Loading Custom Fonts
Use the useFonts hook:
import { useFonts, matchFont } from "@shopify/react-native-skia";
export default function CustomFontExample() {
const fontsLoaded = useFonts({
"Roboto-Regular": [require("./fonts/Roboto-Regular.ttf")],
"Roboto-Bold": [require("./fonts/Roboto-Bold.ttf")],
});
const font = matchFont({
fontFamily: "Roboto",
fontSize: 20,
});
if (!fontsLoaded) return null;
return (
<Canvas style={{ flex: 1 }}>
<Text x={50} y={100} text="Custom Font" font={font} />
</Canvas>
);
}
Imperative Font Creation
import { Skia, FontStyle } from "@shopify/react-native-skia";
const typeface = Skia.Typeface.MakeFreeTypeFaceFromData(
require("./fonts/MyFont.ttf")
);
const font = Skia.Font(typeface, 24);
TextPath
Renders text along a path.
import { Canvas, TextPath, Path, matchFont } from "@shopify/react-native-skia";
const font = matchFont({ fontSize: 20 });
const path = "M 50 150 Q 150 50 250 150";
<Canvas style={{ flex: 1 }}>
<Path path={path} style="stroke" strokeWidth={1} color="#ccc" />
<TextPath
text="Text along a curved path"
path={path}
font={font}
color="blue"
/>
</Canvas>
Props
Offset along the path where text starts
Glyphs
Render individual glyphs with precise positioning.
import { Canvas, Glyphs, matchFont } from "@shopify/react-native-skia";
const font = matchFont({ fontSize: 32 });
const glyphIDs = font.getGlyphIDs("Hello");
const glyphs = glyphIDs.map((id, i) => ({
id,
pos: { x: 50 + i * 25, y: 100 },
}));
<Canvas style={{ flex: 1 }}>
<Glyphs glyphs={glyphs} x={0} y={0} font={font} color="purple" />
</Canvas>
Props
Array of glyph objects with { id: number, pos: { x: number, y: number } }
Font containing the glyphs
TextBlob
Render pre-shaped text for better performance.
import { Canvas, TextBlob, Skia, matchFont } from "@shopify/react-native-skia";
const font = matchFont({ fontSize: 24 });
const textBlob = Skia.TextBlob.MakeFromText("Optimized Text", font);
<Canvas style={{ flex: 1 }}>
<TextBlob blob={textBlob} x={50} y={100} color="green" />
</Canvas>
Props
Paragraph
Render multi-line text with advanced layout.
import { Canvas, Paragraph, Skia, useFonts } from "@shopify/react-native-skia";
export default function ParagraphExample() {
const fontsLoaded = useFonts({
Roboto: [require("./Roboto-Regular.ttf")],
});
if (!fontsLoaded) return null;
const paragraphStyle = {
textAlign: TextAlign.Left,
};
const textStyle = {
color: Skia.Color("black"),
fontFamilies: ["Roboto"],
fontSize: 16,
};
const paragraph = Skia.ParagraphBuilder.Make(paragraphStyle)
.pushStyle(textStyle)
.addText("This is a paragraph with automatic line wrapping. ")
.addText("It can contain multiple lines and styles.")
.pop()
.build();
paragraph.layout(250); // Max width
return (
<Canvas style={{ flex: 1 }}>
<Paragraph paragraph={paragraph} x={50} y={50} width={250} />
</Canvas>
);
}
Font Metrics
Get measurements from fonts:
const font = matchFont({ fontSize: 24 });
// Get text width
const width = font.getTextWidth("Hello");
// Get bounding box
const bounds = font.measureText("Hello");
console.log(bounds); // { x, y, width, height }
// Get font metrics
const metrics = font.getMetrics();
console.log(metrics); // { ascent, descent, leading }
// Get glyph IDs
const glyphIDs = font.getGlyphIDs("Hello");
// Get glyph widths
const widths = font.getGlyphWidths(glyphIDs);
Text Effects
Gradient Text
import { Canvas, Text, LinearGradient, vec, matchFont } from "@shopify/react-native-skia";
const font = matchFont({ fontSize: 48, fontWeight: "bold" });
<Canvas style={{ flex: 1 }}>
<Text x={50} y={100} text="GRADIENT" font={font}>
<LinearGradient
start={vec(0, 0)}
end={vec(300, 0)}
colors={["#FF0080", "#7928CA", "#0070F3"]}
/>
</Text>
</Canvas>
Stroked Text
const font = matchFont({ fontSize: 48, fontWeight: "bold" });
<Canvas style={{ flex: 1 }}>
{/* Outline */}
<Text
x={50}
y={100}
text="OUTLINE"
font={font}
color="blue"
style="stroke"
strokeWidth={2}
/>
{/* Fill */}
<Text x={50} y={100} text="OUTLINE" font={font} color="white" />
</Canvas>
Shadow Text
import { Canvas, Text, Shadow, matchFont } from "@shopify/react-native-skia";
const font = matchFont({ fontSize: 36 });
<Canvas style={{ flex: 1 }}>
<Text x={50} y={100} text="Shadowed" font={font} color="black">
<Shadow dx={2} dy={2} blur={4} color="rgba(0,0,0,0.5)" />
</Text>
</Canvas>
- Use
TextBlob for static text that doesn’t change
- Load fonts once and reuse font objects
- Use
useMemo to cache font creation
- Avoid creating new font instances in render
- For dynamic text, consider using the simpler
Text component
Font Styling Options
const font = matchFont({
fontFamily: "System",
fontSize: 20,
fontStyle: "normal" | "italic",
fontWeight: "normal" | "bold" | 100-900,
});