Skip to main content
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

text
string
required
The text string to render
x
number
required
X-coordinate of the text baseline start
y
number
required
Y-coordinate of the text baseline
font
SkFont
required
Font object created with matchFont() or Skia.Font()
color
Color
Text color

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

text
string
required
The text to render
path
string | SkPath
required
The path to follow
font
SkFont
required
Font to use
initialOffset
number
default:"0"
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

glyphs
Glyph[]
required
Array of glyph objects with { id: number, pos: { x: number, y: number } }
x
number
required
X offset for all glyphs
y
number
required
Y offset for all glyphs
font
SkFont
required
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

blob
SkTextBlob
required
Pre-created text blob
x
number
required
X-coordinate
y
number
required
Y-coordinate

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>

Performance Tips

  • 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,
});

Build docs developers (and LLMs) love