Skip to main content
The ImageShader component allows you to use images as shaders, providing control over tiling, fitting, and transformations. This enables you to create patterns, textures, and complex visual effects.

Image Shader

The ImageShader returns an image as a shader with configurable tiling modes. It uses cubic sampling for high-quality rendering.

Props

NameTypeDescription
imageSkImageThe image instance to use as a shader
txTileModeHorizontal tiling mode: clamp, repeat, mirror, or decal (default: decal)
tyTileModeVertical tiling mode: clamp, repeat, mirror, or decal (default: decal)
fitFitHow to fit the image in the destination rectangle (default: none)
rectSkRectDestination rectangle for fit calculation
transformTransform2dAdditional transformations to apply (see transformations)
samplingSamplingSampling method for the image (see sampling options)

Basic Example

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

const ImageShaderDemo = () => {
  const image = useImage(require("./assets/oslo.jpg"));
  
  if (!image) {
    return null;
  }
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Circle cx={128} cy={128} r={128}>
        <ImageShader
          image={image}
          fit="cover"
          rect={{ x: 0, y: 0, width: 256, height: 256 }}
        />
      </Circle>
    </Canvas>
  );
};

Tiling Modes

Control how the image repeats using tile modes:

Clamp

Extends edge pixels:
<ImageShader image={image} tx="clamp" ty="clamp" />

Repeat

Repeats the image:
import { Canvas, Rect, ImageShader, useImage } from "@shopify/react-native-skia";

const RepeatPattern = () => {
  const image = useImage(require("./assets/pattern.png"));
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={256} height={256}>
        <ImageShader image={image} tx="repeat" ty="repeat" />
      </Rect>
    </Canvas>
  );
};

Mirror

Mirrors the image:
<ImageShader image={image} tx="mirror" ty="mirror" />

Decal

Draws the image once, transparent elsewhere:
<ImageShader image={image} tx="decal" ty="decal" />

Fitting Images

The fit property works with the rect property to control how the image fits within a rectangle:

Contain

Fits the entire image within the bounds:
<ImageShader
  image={image}
  fit="contain"
  rect={{ x: 0, y: 0, width: 256, height: 256 }}
/>

Cover

Covers the entire rectangle, may crop the image:
<ImageShader
  image={image}
  fit="cover"
  rect={{ x: 0, y: 0, width: 256, height: 256 }}
/>

Fill

Stretches the image to fill the rectangle:
<ImageShader
  image={image}
  fit="fill"
  rect={{ x: 0, y: 0, width: 256, height: 256 }}
/>

Other Fit Options

  • fitWidth: Scales to match width
  • fitHeight: Scales to match height
  • scaleDown: Like contain, but never scales up
  • none: No fitting applied (default)

Transformations

Apply transformations to image shaders:
import { Canvas, Rect, ImageShader, useImage } from "@shopify/react-native-skia";

const TransformedImage = () => {
  const image = useImage(require("./assets/texture.jpg"));
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={256} height={256}>
        <ImageShader
          image={image}
          tx="repeat"
          ty="repeat"
          transform={[
            { scale: 0.5 },
            { rotate: Math.PI / 4 }
          ]}
        />
      </Rect>
    </Canvas>
  );
};

Sampling Options

Control image quality with sampling options:

Cubic Sampling (Best Quality)

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

const HighQuality = () => {
  const image = useImage(require("./assets/photo.jpg"));
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={256} height={256}>
        <ImageShader
          image={image}
          fit="cover"
          rect={{ x: 0, y: 0, width: 256, height: 256 }}
          sampling={CubicSampling}
        />
      </Rect>
    </Canvas>
  );
};

Filter Modes

import { FilterMode, MipmapMode } from "@shopify/react-native-skia";

<ImageShader
  image={image}
  sampling={{ 
    filter: FilterMode.Linear, 
    mipmap: MipmapMode.Linear 
  }}
/>

Creating Patterns

Tiled Pattern

import { Canvas, Fill, ImageShader, useImage } from "@shopify/react-native-skia";

const TiledPattern = () => {
  const pattern = useImage(require("./assets/tile.png"));
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Fill>
        <ImageShader
          image={pattern}
          tx="repeat"
          ty="repeat"
        />
      </Fill>
    </Canvas>
  );
};

Kaleidoscope Effect

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

const Kaleidoscope = () => {
  const image = useImage(require("./assets/photo.jpg"));
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={256} height={256}>
        <ImageShader
          image={image}
          tx="mirror"
          ty="mirror"
          fit="cover"
          rect={{ x: 0, y: 0, width: 128, height: 128 }}
        />
      </Rect>
    </Canvas>
  );
};

Combining with Other Shaders

Blend image shaders with other effects:
import { 
  Canvas, 
  Rect, 
  Blend, 
  ImageShader, 
  LinearGradient,
  useImage,
  vec 
} from "@shopify/react-native-skia";

const BlendedImage = () => {
  const image = useImage(require("./assets/photo.jpg"));
  
  return (
    <Canvas style={{ flex: 1 }}>
      <Rect x={0} y={0} width={256} height={256}>
        <Blend mode="multiply">
          <ImageShader
            image={image}
            fit="cover"
            rect={{ x: 0, y: 0, width: 256, height: 256 }}
          />
          <LinearGradient
            start={vec(0, 0)}
            end={vec(0, 256)}
            colors={["rgba(255,255,255,0)", "rgba(0,0,0,0.8)"]}
          />
        </Blend>
      </Rect>
    </Canvas>
  );
};

Using with Runtime Shaders

Image shaders can be passed to custom runtime shaders:
import { 
  Canvas, 
  Fill, 
  Shader, 
  ImageShader,
  Skia, 
  useImage 
} from "@shopify/react-native-skia";

const source = Skia.RuntimeEffect.Make(`
  uniform shader image;
  
  half4 main(float2 xy) {
    xy.x += sin(xy.y / 3) * 4;
    return image.eval(xy);
  }
`)!;

const WavyImage = () => {
  const image = useImage(require("./assets/oslo.jpg"));
  
  if (!image) return null;
  
  return (
    <Canvas style={{ width: 256, height: 256 }}>
      <Fill>
        <Shader source={source}>
          <ImageShader
            image={image}
            fit="cover"
            rect={{ x: 0, y: 0, width: 256, height: 256 }}
          />
        </Shader>
      </Fill>
    </Canvas>
  );
};

Performance Tips

  • Reuse SkImage instances when possible
  • Use appropriate sampling modes for your use case
  • Consider using lower resolution images for patterns
  • Cache images loaded with useImage
  • Use decal tile mode when you don’t need tiling

Loading Images

Images are loaded using the useImage hook:
import { useImage } from "@shopify/react-native-skia";

// From bundle
const image1 = useImage(require("./assets/photo.jpg"));

// From network
const image2 = useImage("https://example.com/image.jpg");

// From native bundle (iOS/Android)
const image3 = useImage("Logo");
See the Images Overview for more details on loading images.

See Also

Build docs developers (and LLMs) love