Paragraph
React Native Skia offers an API to perform text layouts using the Skia Paragraph API.
Hello World
In the example below, we create a simple paragraph based on custom fonts. The emojis will be renderer using the emoji font available on the platform. Other system fonts will are available as well.
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,useFonts ,TextAlign ,Canvas } from "@shopify/react-native-skia";constMyParagraph = () => {constcustomFontMgr =useFonts ({Roboto : [require ("path/to/Roboto-Regular.ttf"),require ("path/to/Roboto-Medium.ttf")]});constparagraph =useMemo (() => {// Are the font loaded already?if (!customFontMgr ) {return null;}constparagraphStyle = {textAlign :TextAlign .Center };consttextStyle = {color :Skia .Color ("black"),fontFamilies : ["Roboto"],fontSize : 50,};returnSkia .ParagraphBuilder .Make (paragraphStyle ,customFontMgr ).pushStyle (textStyle ).addText ("Say Hello to ").pushStyle ({ ...textStyle ,fontStyle : {weight : 500 } }).addText ("Skia 🎨").pop ().build ();}, [customFontMgr ]);// Render the paragraphreturn (<Canvas style ={{width : 256,height : 256 }}><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={300} /></Canvas >);};
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,useFonts ,TextAlign ,Canvas } from "@shopify/react-native-skia";constMyParagraph = () => {constcustomFontMgr =useFonts ({Roboto : [require ("path/to/Roboto-Regular.ttf"),require ("path/to/Roboto-Medium.ttf")]});constparagraph =useMemo (() => {// Are the font loaded already?if (!customFontMgr ) {return null;}constparagraphStyle = {textAlign :TextAlign .Center };consttextStyle = {color :Skia .Color ("black"),fontFamilies : ["Roboto"],fontSize : 50,};returnSkia .ParagraphBuilder .Make (paragraphStyle ,customFontMgr ).pushStyle (textStyle ).addText ("Say Hello to ").pushStyle ({ ...textStyle ,fontStyle : {weight : 500 } }).addText ("Skia 🎨").pop ().build ();}, [customFontMgr ]);// Render the paragraphreturn (<Canvas style ={{width : 256,height : 256 }}><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={300} /></Canvas >);};
Below is the result on Android (left) and iOS (right).
On Web, you will need to provide you own emoji font (NotoColorEmoji for instance) and add it to the list of font families.
tsx
import {useFonts ,Skia } from "@shopify/react-native-skia";constcustomFontMgr =useFonts ({Roboto : [require ("path/to/Roboto-Regular.ttf"),require ("path/to/Roboto-Medium.ttf")],// Only load the emoji font on WebNoto : [require ("path/to/NotoColorEmoji.ttf")]});// We add Noto to the list of font familiesconsttextStyle = {color :Skia .Color ("black"),fontFamilies : ["Roboto", "Noto"],fontSize : 50,};
tsx
import {useFonts ,Skia } from "@shopify/react-native-skia";constcustomFontMgr =useFonts ({Roboto : [require ("path/to/Roboto-Regular.ttf"),require ("path/to/Roboto-Medium.ttf")],// Only load the emoji font on WebNoto : [require ("path/to/NotoColorEmoji.ttf")]});// We add Noto to the list of font familiesconsttextStyle = {color :Skia .Color ("black"),fontFamilies : ["Roboto", "Noto"],fontSize : 50,};
Using Paints
You can use paint objects for the foreground and the background of a text style.
Below we use a foreground and a background paint on a text style:
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,useFonts ,Canvas ,Rect ,TileMode } from "@shopify/react-native-skia";// Our background shaderconstsource =Skia .RuntimeEffect .Make (`uniform vec4 position;uniform vec4 colors[4];vec4 main(vec2 pos) {vec2 uv = (pos - vec2(position.x, position.y))/vec2(position.z, position.w);vec4 colorA = mix(colors[0], colors[1], uv.x);vec4 colorB = mix(colors[2], colors[3], uv.x);return mix(colorA, colorB, uv.y);}`)!;// Define an array of colors for the gradient to be used in shader uniformconstcolors = [// #dafb610.85, 0.98, 0.38, 1.0,// #61dafb0.38, 0.85, 0.98, 1.0,// #fb61da0.98, 0.38, 0.85, 1.0,// #61fbcf0.38, 0.98, 0.81, 1.0];constMyParagraph = () => {constparagraph =useMemo (() => {// Create a background paint.constbackgroundPaint =Skia .Paint ();backgroundPaint .setShader (source .makeShader ([0, 0, 256, 256, ...colors ]));// Create a foreground paint. We use a radial gradient.constforegroundPaint =Skia .Paint ();foregroundPaint .setShader (Skia .Shader .MakeRadialGradient ({x : 0,y : 0 },256,[Skia .Color ("magenta"),Skia .Color ("yellow")],null,TileMode .Clamp ));constpara =Skia .ParagraphBuilder .Make ().pushStyle ({fontFamilies : ["Roboto"],fontSize : 72,fontStyle : {weight : 500 },color :Skia .Color ("black"),},foregroundPaint ,backgroundPaint ).addText ("Say Hello to React Native Skia").pop ().build ();returnpara ;}, []);return (<Canvas style ={{width : 256,height : 256 }}><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={256} /></Canvas >);};
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,useFonts ,Canvas ,Rect ,TileMode } from "@shopify/react-native-skia";// Our background shaderconstsource =Skia .RuntimeEffect .Make (`uniform vec4 position;uniform vec4 colors[4];vec4 main(vec2 pos) {vec2 uv = (pos - vec2(position.x, position.y))/vec2(position.z, position.w);vec4 colorA = mix(colors[0], colors[1], uv.x);vec4 colorB = mix(colors[2], colors[3], uv.x);return mix(colorA, colorB, uv.y);}`)!;// Define an array of colors for the gradient to be used in shader uniformconstcolors = [// #dafb610.85, 0.98, 0.38, 1.0,// #61dafb0.38, 0.85, 0.98, 1.0,// #fb61da0.98, 0.38, 0.85, 1.0,// #61fbcf0.38, 0.98, 0.81, 1.0];constMyParagraph = () => {constparagraph =useMemo (() => {// Create a background paint.constbackgroundPaint =Skia .Paint ();backgroundPaint .setShader (source .makeShader ([0, 0, 256, 256, ...colors ]));// Create a foreground paint. We use a radial gradient.constforegroundPaint =Skia .Paint ();foregroundPaint .setShader (Skia .Shader .MakeRadialGradient ({x : 0,y : 0 },256,[Skia .Color ("magenta"),Skia .Color ("yellow")],null,TileMode .Clamp ));constpara =Skia .ParagraphBuilder .Make ().pushStyle ({fontFamilies : ["Roboto"],fontSize : 72,fontStyle : {weight : 500 },color :Skia .Color ("black"),},foregroundPaint ,backgroundPaint ).addText ("Say Hello to React Native Skia").pop ().build ();returnpara ;}, []);return (<Canvas style ={{width : 256,height : 256 }}><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={256} /></Canvas >);};
Applying Effects
The Paragraph
component doesn't follow the same painting rules as other components.
However you can apply effets using the layer
property.
For instance, in the example below, fopr we apply a blur image filter.
tsx
importReact from "react";import {Canvas ,Skia ,Group ,Paint ,Blur ,Paragraph } from "@shopify/react-native-skia";constwidth = 256;constheight = 256;export constDemo = () => {constparagraph =Skia .ParagraphBuilder .Make ().pushStyle ({color :Skia .Color ("black"),fontSize : 25,}).addText ("Hello Skia").build ();return (<Canvas style ={{flex : 1 }}><Group layer ={<Paint ><Blur blur ={10} /></Paint >}><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={width } /></Group ></Canvas >);};
tsx
importReact from "react";import {Canvas ,Skia ,Group ,Paint ,Blur ,Paragraph } from "@shopify/react-native-skia";constwidth = 256;constheight = 256;export constDemo = () => {constparagraph =Skia .ParagraphBuilder .Make ().pushStyle ({color :Skia .Color ("black"),fontSize : 25,}).addText ("Hello Skia").build ();return (<Canvas style ={{flex : 1 }}><Group layer ={<Paint ><Blur blur ={10} /></Paint >}><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={width } /></Group ></Canvas >);};
Result
Paragraph Bounding Box
Before getting the paragraph height and width, you need to compute its layout using layout()
and and once done, you can invoke getHeight()
for the height and getLongestLine()
for the width.
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,useFonts ,Canvas ,Rect } from "@shopify/react-native-skia";constMyParagraph = () => {constparagraph =useMemo (() => {constpara =Skia .ParagraphBuilder .Make ().addText ("Say Hello to React Native Skia").build ();// Calculate the layoutpara .layout (200);returnpara ;}, []);// Now the paragraph height is availableconstheight =paragraph .getHeight ();constwidth =paragraph .getLongestLine ();// Render the paragraphreturn (<Canvas style ={{width : 256,height : 256 }}>{/* Maximum paragraph width */}<Rect x ={0}y ={0}width ={200}height ={256}color ="magenta" />{/* Paragraph bounding box */}<Rect x ={0}y ={0}width ={width }height ={height }color ="cyan" /><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={200} /></Canvas >);};
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,useFonts ,Canvas ,Rect } from "@shopify/react-native-skia";constMyParagraph = () => {constparagraph =useMemo (() => {constpara =Skia .ParagraphBuilder .Make ().addText ("Say Hello to React Native Skia").build ();// Calculate the layoutpara .layout (200);returnpara ;}, []);// Now the paragraph height is availableconstheight =paragraph .getHeight ();constwidth =paragraph .getLongestLine ();// Render the paragraphreturn (<Canvas style ={{width : 256,height : 256 }}>{/* Maximum paragraph width */}<Rect x ={0}y ={0}width ={200}height ={256}color ="magenta" />{/* Paragraph bounding box */}<Rect x ={0}y ={0}width ={width }height ={height }color ="cyan" /><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={200} /></Canvas >);};
Fonts
By default, the paragraph API will use the system fonts. You can also use custom fonts with this API was well.
The useFonts
hooks allows you to load custom fonts to be used for your Skia drawing.
The font files should be organized by family names.
For example:
tsx
import {useFonts } from "@shopify/react-native-skia";constfontMgr =useFonts ({Roboto : [require ("./Roboto-Medium.ttf"),require ("./Roboto-Regular.ttf"),require ("./Roboto-Bold.ttf"),],Helvetica : [require ("./Helvetica.ttf")],});if (!fontMgr ) {// Returns null until all fonts are loaded}// Now the fonts are available
tsx
import {useFonts } from "@shopify/react-native-skia";constfontMgr =useFonts ({Roboto : [require ("./Roboto-Medium.ttf"),require ("./Roboto-Regular.ttf"),require ("./Roboto-Bold.ttf"),],Helvetica : [require ("./Helvetica.ttf")],});if (!fontMgr ) {// Returns null until all fonts are loaded}// Now the fonts are available
You can also list the available system fonts via listFontFamilies()
function.
Styling Paragraphs
These properties define the overall layout and behavior of a paragraph.
Property | Description |
---|---|
disableHinting | Controls whether text hinting is disabled. |
ellipsis | Specifies the text to use for ellipsis when text overflows. |
heightMultiplier | Sets the line height as a multiplier of the font size. |
maxLines | Maximum number of lines for the paragraph. |
replaceTabCharacters | Determines whether tab characters should be replaced with spaces. |
strutStyle | Defines the strut style, which affects the minimum height of a line. |
textAlign | Sets the alignment of text (left, right, center, justify, start, end). |
textDirection | Determines the text direction (RTL or LTR). |
textHeightBehavior | Controls the behavior of text ascent and descent in the first and last lines. |
textStyle | Default text style for the paragraph (can be overridden by individual text styles). |
Below is an example to center text with textAlign
property:
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,TextAlign ,Canvas ,Rect } from "@shopify/react-native-skia";constMyParagraph = () => {constparagraph =useMemo (() => {constpara =Skia .ParagraphBuilder .Make ({textAlign :TextAlign .Center ,}).addText ("Say Hello to React Native Skia").build ();returnpara ;}, []);// Render the paragraph with the text centerreturn (<Canvas style ={{width : 256,height : 256 }}><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={200} /></Canvas >);};
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,TextAlign ,Canvas ,Rect } from "@shopify/react-native-skia";constMyParagraph = () => {constparagraph =useMemo (() => {constpara =Skia .ParagraphBuilder .Make ({textAlign :TextAlign .Center ,}).addText ("Say Hello to React Native Skia").build ();returnpara ;}, []);// Render the paragraph with the text centerreturn (<Canvas style ={{width : 256,height : 256 }}><Paragraph paragraph ={paragraph }x ={0}y ={0}width ={200} /></Canvas >);};
Text Style Properties
These properties are used to style specific segments of text within a paragraph.
Property | Description |
---|---|
backgroundColor | Background color of the text. |
color | Color of the text. |
decoration | Type of text decoration (underline, overline, line-through). |
decorationColor | Color of the text decoration. |
decorationThickness | Thickness of the text decoration. |
decorationStyle | Style of the text decoration (solid, double, dotted, dashed, wavy). |
fontFamilies | List of font families for the text. |
fontFeatures | List of font features. |
fontSize | Font size of the text. |
fontStyle | Font style (weight, width, slant). |
fontVariations | Font variations. |
foregroundColor | Foreground color (for effects like gradients). |
heightMultiplier | Multiplier for line height. |
halfLeading | Controls half-leading value. |
letterSpacing | Space between characters. |
locale | Locale for the text (affects things like sorting). |
shadows | List of text shadows. |
textBaseline | Baseline for the text (alphabetic, ideographic). |
wordSpacing | Space between words. |
These tables offer a quick reference to differentiate between paragraph and text styles in React Native Skia. You can use them to guide developers on how to apply various styles to create visually appealing and functional text layouts. Below is an example using different font styling:
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,useFonts ,FontStyle } from "@shopify/react-native-skia";constMyParagraph = () => {constcustomFontMgr =useFonts ({Roboto : [require ("path/to/Roboto-Italic.ttf"),require ("path/to/Roboto-Regular.ttf"),require ("path/to/Roboto-Bold.ttf")],});constparagraph =useMemo (() => {// Are the custom fonts loaded?if (!customFontMgr ) {return null;}consttextStyle = {fontSize : 24,fontFamilies : ["Roboto"],color :Skia .Color ("#000"),};constparagraphBuilder =Skia .ParagraphBuilder .Make ({},customFontMgr );paragraphBuilder .pushStyle ({ ...textStyle ,fontStyle :FontStyle .Bold }).addText ("This text is bold\n").pop ().pushStyle ({ ...textStyle ,fontStyle :FontStyle .Normal }).addText ("This text is regular\n").pop ().pushStyle ({ ...textStyle ,fontStyle :FontStyle .Italic }).addText ("This text is italic").pop ().build ();returnparagraphBuilder .build ();}, [customFontMgr ]);return <Paragraph paragraph ={paragraph }x ={0}y ={0}width ={300} />;};
tsx
import {useMemo } from "react";import {Paragraph ,Skia ,useFonts ,FontStyle } from "@shopify/react-native-skia";constMyParagraph = () => {constcustomFontMgr =useFonts ({Roboto : [require ("path/to/Roboto-Italic.ttf"),require ("path/to/Roboto-Regular.ttf"),require ("path/to/Roboto-Bold.ttf")],});constparagraph =useMemo (() => {// Are the custom fonts loaded?if (!customFontMgr ) {return null;}consttextStyle = {fontSize : 24,fontFamilies : ["Roboto"],color :Skia .Color ("#000"),};constparagraphBuilder =Skia .ParagraphBuilder .Make ({},customFontMgr );paragraphBuilder .pushStyle ({ ...textStyle ,fontStyle :FontStyle .Bold }).addText ("This text is bold\n").pop ().pushStyle ({ ...textStyle ,fontStyle :FontStyle .Normal }).addText ("This text is regular\n").pop ().pushStyle ({ ...textStyle ,fontStyle :FontStyle .Italic }).addText ("This text is italic").pop ().build ();returnparagraphBuilder .build ();}, [customFontMgr ]);return <Paragraph paragraph ={paragraph }x ={0}y ={0}width ={300} />;};