Skip to main content
Anytime you draw something in Skia, you specify painting attributes like color, blend mode, or style. These attributes control how shapes, text, and images appear on the canvas.

Applying Paint Attributes

Paint attributes can be applied in three ways:
  1. As properties on drawing components or groups
  2. As children of drawing components or groups
  3. Via Paint component reference for manual assignment

Property-Based Attributes

These attributes can be set directly as props:

Child-Based Attributes

These attributes are applied as children:

Fills and Strokes

In Skia, paint has a style property indicating whether it’s a fill or stroke. You can apply multiple paints to a single shape:
import { Canvas, Circle, Paint, vec } from "@shopify/react-native-skia";

const width = 256;
const height = 256;

const PaintDemo = () => {
  const strokeWidth = 10;
  const c = vec(width / 2, height / 2);
  const r = (width - strokeWidth) / 2;
  
  return (
    <Canvas style={{ width, height }}>
      <Circle c={c} r={r}>
        <Paint color="lightblue" />
        <Paint color="#adbce6" style="stroke" strokeWidth={strokeWidth} />
        <Paint color="#ade6d8" style="stroke" strokeWidth={strokeWidth / 2} />
      </Circle>
    </Canvas>
  );
};
This circle has:
  • One light blue fill
  • Two different stroke paints with different colors and widths

Inheritance

Descendants inherit paint attributes from their ancestors:
import { Canvas, Circle, Group } from "@shopify/react-native-skia";

const width = 256;
const height = 256;

const PaintDemo = () => {
  const r = width / 6;
  
  return (
    <Canvas style={{ width, height }}>
      <Group color="lightblue">
        <Circle cx={r} cy={r} r={r} />
        <Group style="stroke" strokeWidth={10}>
          <Circle cx={3 * r} cy={3 * r} r={r} />
        </Group>
      </Group>
    </Canvas>
  );
};
  • The first circle inherits the lightblue color as a fill
  • The second circle inherits both the color and the stroke styling

Complex Paint Attributes

Shaders and filters are passed as children:
import { Canvas, Circle, LinearGradient, vec } from "@shopify/react-native-skia";

const width = 256;
const height = 256;

const PaintDemo = () => {
  const r = width / 2;
  
  return (
    <Canvas style={{ width, height }}>
      <Circle cx={r} cy={r} r={r}>
        <LinearGradient
          start={vec(0, 0)}
          end={vec(2 * r, 2 * r)}
          colors={["#00ff87", "#60efff"]}
        />
      </Circle>
    </Canvas>
  );
};

Inherited Complex Attributes

Complex attributes also inherit:
import { Canvas, Circle, Group, LinearGradient, vec } from "@shopify/react-native-skia";

const width = 256;
const height = 256;

const PaintDemo = () => {
  const r = width / 2;
  
  return (
    <Canvas style={{ width, height }}>
      <Group>
        <LinearGradient
          start={vec(0, 0)}
          end={vec(2 * r, 2 * r)}
          colors={["#0061ff", "#60efff"]}
        />
        <Circle cx={r} cy={r} r={r} />
        <Circle cx={3 * r} cy={r} r={r / 2} />
      </Group>
    </Canvas>
  );
};

Manual Paint Assignment

For advanced use cases, create paint instances manually:
import { Canvas, Circle, Skia } from "@shopify/react-native-skia";

const width = 256;
const height = 256;
const r = width / 2;

// Create paint instance
const paint = Skia.Paint();
paint.setColor(Skia.Color("lightblue"));
paint.setAntiAlias(true);

const PaintDemo = () => {
  return (
    <Canvas style={{ flex: 1 }}>
      <Circle paint={paint} cx={r} cy={r} r={r} />
    </Canvas>
  );
};

Common Patterns

Multiple Fills with Opacity

import { Canvas, Rect, Paint } from "@shopify/react-native-skia";

const LayeredFills = () => (
  <Canvas style={{ width: 256, height: 256 }}>
    <Rect x={50} y={50} width={156} height={156}>
      <Paint color="red" opacity={0.5} />
      <Paint color="blue" opacity={0.5} />
    </Rect>
  </Canvas>
);

Stroke Over Fill

import { Canvas, Circle, Paint } from "@shopify/react-native-skia";

const StrokedCircle = () => (
  <Canvas style={{ width: 256, height: 256 }}>
    <Circle cx={128} cy={128} r={100}>
      <Paint color="#e3f2fd" />
      <Paint color="#2196f3" style="stroke" strokeWidth={4} />
    </Circle>
  </Canvas>
);

Gradient with Stroke

import { Canvas, Rect, Paint, LinearGradient, vec } from "@shopify/react-native-skia";

const GradientRect = () => (
  <Canvas style={{ width: 256, height: 256 }}>
    <Rect x={50} y={50} width={156} height={156}>
      <LinearGradient
        start={vec(50, 50)}
        end={vec(206, 206)}
        colors={["#667eea", "#764ba2"]}
      />
      <Paint style="stroke" strokeWidth={2} color="white" />
    </Rect>
  </Canvas>
);

Multiple Strokes

import { Canvas, Circle, Paint } from "@shopify/react-native-skia";

const MultiStroke = () => (
  <Canvas style={{ width: 256, height: 256 }}>
    <Circle cx={128} cy={128} r={100}>
      <Paint color="white" />
      <Paint color="#000" style="stroke" strokeWidth={10} />
      <Paint color="#fff" style="stroke" strokeWidth={6} />
      <Paint color="#000" style="stroke" strokeWidth={2} />
    </Circle>
  </Canvas>
);

Paint Priority

When multiple paint attributes are specified:
  1. Direct props on the component have highest priority
  2. Paint children are applied in order
  3. Inherited attributes from parent groups apply if not overridden
import { Canvas, Circle, Group, Paint } from "@shopify/react-native-skia";

const PaintPriority = () => (
  <Canvas style={{ width: 256, height: 256 }}>
    <Group color="red">
      <Circle cx={64} cy={128} r={50} /> {/* Red (inherited) */}
      <Circle cx={128} cy={128} r={50} color="blue" /> {/* Blue (prop) */}
      <Circle cx={192} cy={128} r={50}>
        <Paint color="green" /> {/* Green (child) */}
      </Circle>
    </Group>
  </Canvas>
);

Performance Tips

  • Reuse paint instances when possible
  • Use groups to apply common attributes to multiple shapes
  • Avoid creating new paint objects every render
  • Cache color values that don’t change
  • Use simple blend modes for better performance

See Also

Build docs developers (and LLMs) love