# Textures

> Load PNG and JPEG images as textures with useTexture, use them as colour images or engraving masks, and choose the right options for colour images, masks and repeating patterns.

`useTexture` loads a PNG or JPEG and returns a three.js texture, or `null` while it loads. Images are decoded natively from their bytes (Hermes has no image decoder of its own), shared between components, and freed when the last user unmounts.

```tsx title="CerealBox.tsx"
import { Box, Scene } from "@apricot/react";
import { useTexture } from "@apricot/react/model";

export function CerealBox() {
  const front = useTexture(require("./assets/cereal-front.png"));
  return (
    <Scene style={{ flex: 1 }} look="studio">
      <Box size={[1.6, 2.2, 0.5]} material={{ preset: "standard", color: "#ffffff", map: front }} />
    </Scene>
  );
}
```

- Import it from `@apricot/react/model`.
- `src` is a bundled image (`require(...)`) or a URL.
- Use `color: "#ffffff"` with a `map`: the image is multiplied by the material's colour, and most presets are not white.
- A `<Box>` shows the image on each of its six faces. For one face, use a `<Plane>` or a model with UVs.

## Options

`useTexture(src, options)`:

| Option | Default | Use |
| --- | --- | --- |
| `colorSpace` | sRGB | Colour images. Pass `NoColorSpace` (from three) for data such as masks and normal maps. |
| `flipY` | `true` | Image rows map top to bottom, as three.js expects. |
| `repeat` | `false` | Tile the image instead of stretching the edge pixels. |
| `premultiplyAlpha` | `true` | Keep colours premultiplied by alpha (what phone decoders produce). Preset materials handle it. Pass `false` only for three.js materials of your own that expect straight colour. |

## Errors

`useTexture` logs failures and returns `null`. To show what went wrong, use `useTextureState`, which returns `{ texture, error }`:

```tsx title="WithError.tsx"
import { Plane } from "@apricot/react";
import { useTextureState } from "@apricot/react/model";

export function Photo({ url }: { url: string }) {
  const { texture, error } = useTextureState(url);
  return <Plane size={[3, 2]} material={{ preset: "standard", color: error ? "#ff4d4f" : "#ffffff", map: texture }} />;
}
```

## Engraving and printing

A mask image (opaque = engraved) passed as `engrave` changes the colour, roughness and metalness where the mask is set. This is how the metal card's art is engraved:

```tsx title="Engraved.tsx"
import { Box, Scene } from "@apricot/react";
import { useTexture } from "@apricot/react/model";

export function Engraved() {
  const mask = useTexture(require("./assets/logo-mask.png"));
  return (
    <Scene style={{ flex: 1 }} look="none" environment="studio" background="#0e0f12">
      <Box
        size={[3, 2, 0.05]}
        radius={0.02}
        material={{
          preset: "brushed-metal",
          engrave: mask ? { mask, color: "#6f737a", roughness: 0.62, metalness: 1 } : null,
        }}
      />
    </Scene>
  );
}
```

For payment cards, `<Card art>` from `@apricot/assets` does this for every finish: see the [card-tilt recipe](https://matthiasbrandolin.dev/apricot/recipes/card-tilt).

> [!NOTE]
> A new texture builds a new material (one shader compile). Load textures once and keep them; change `color` instead when you can.

## Related

- [Shapes and materials](https://matthiasbrandolin.dev/apricot/guides/shapes-and-materials).
- [Model reference](https://matthiasbrandolin.dev/apricot/reference/model): `useTexture`, `useTextureState`.
