---
name: illlustrations-avatars
description: Add seeded SVG avatars (the Croods style) to a JavaScript or React app with @illlustrations/avatars. Use when the user wants user avatars, profile picture fallbacks, avatar pickers or placeholder people, or mentions illlustrations, Croods or @illlustrations/avatars.
---

# @illlustrations/avatars

Seeded SVG avatars, rendered locally. No API key or network request.
Version 1.1.0 (Croods style 1.0.0). Human docs: https://illlustrations.co/docs

## Imports

| Import | Contents |
| --- | --- |
| `@illlustrations/avatars` | createAvatar, fromJSON and types. No React. |
| `@illlustrations/avatars/react` | The Avatar component (React 18.3+ or 19). |
| `@illlustrations/avatars/croods` | The Croods style. Future styles get their own path. |
| `@illlustrations/avatars/croods/presets.json` | 24 resolved preset states. |
| `@illlustrations/avatars/svg/croods-001.svg` | Preset SVG files, 001 to 024. |

There is no default export and no `/core`, `/styles/...` or `/dist/...` path.

## Rules

1. Use only part IDs from the parts table (or /docs/parts.json). Unknown IDs throw. `head` and `upperBody` must be real IDs; `face`, `facialHair` and `accessories` also accept `'none'`.
2. Seed with a stable ID (user ID), never a display name or `Math.random()`, so the avatar is identical on server and client and across sessions.
3. When users choose parts, save `createAvatar(...).toJSON()` and restore with `fromJSON` or by passing `selections`, `colors`, `theme`, `background` and `shape` back. A seed alone cannot restore explicit choices.
4. In React, pass the style as `assets={croods}`. `style` is the normal React CSS prop.
5. Several inline `toString()` SVGs on one page need unique `idPrefix` values (letter first; letters, digits, `_`, `-`). `<Avatar>` and `toDataUri()` don't.
6. Colors: `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `'transparent'`, or `'original'` for color roles. Named CSS colors throw.
7. `<Avatar>` is a client component. In a React Server Component, render it as-is or use `createAvatar(...).toDataUri()` in an `<img>`.
8. For email and social images, convert to PNG on the server; many clients don't render SVG.
9. For brand colors, pass a list instead of one color: `colors: { clothing: ['#254CE9', '#F76549'] }`, `background: [...]`. The seed picks one entry, so each person keeps theirs. Never pick colors with `Math.random()`.
10. To recolor something that isn't a role (bag, mouth, drool, accents), use `palette` with keys from `croods.palette` or `avatar.sourceColors()`. Never guess artwork hex values. `#FFFFFF` is both skin and eye whites, so change skin with `colors.skin`.
11. The artwork is CC BY 4.0. Credit it once per app or site, somewhere visible such as the footer, about or credits page: "Croods by illlustrations" linking to https://illlustrations.co. Individual avatars, emails and exported SVGs need no caption.

## API

```ts
createAvatar(style, options?)   // → { toString(opts?), toDataUri(opts?), toJSON() }
  .toString({ idPrefix?, title? })  // SVG string
  .toDataUri({ title? })            // data:image/svg+xml,… for <img src>
  .toJSON()                         // AvatarState: save this to restore later
  .sourceColors()                   // artwork colors in this avatar (palette keys)
fromJSON(style, state)          // → same object as createAvatar; validates the state
<Avatar assets={style} {...options} title? className? style? />   // React
```

Types exported from `@illlustrations/avatars`: `AvatarOptions`, `AvatarState`, `AvatarStyle`, `Selections`, `Colors`, `Palette`, `Slot`, `Shape`, `Theme`. `style.palette` lists every artwork color.

## Minimal usage

```tsx
import { Avatar } from '@illlustrations/avatars/react';
import { croods } from '@illlustrations/avatars/croods';

export function UserAvatar() {
  return (
    <Avatar
      assets={croods}
      seed="alex"
      size={64}
      shape="circle"
      background="#EDEDFF"
      title="Alex's avatar"
    />
  );
}
```

```ts
import { createAvatar } from '@illlustrations/avatars';
import { croods } from '@illlustrations/avatars/croods';

const avatar = createAvatar(croods, {
  seed: 'alex',
  size: 64,
  shape: 'circle',
  background: '#EDEDFF',
});

// Use an image in your page. No React required.
const image = document.createElement('img');
image.src = avatar.toDataUri();
image.alt = "Alex's avatar";
document.body.append(image);
```

```ts
import { createAvatar, fromJSON } from '@illlustrations/avatars';
import { croods } from '@illlustrations/avatars/croods';

const avatar = createAvatar(croods, {
  seed: 'alex',
  selections: { head: 'straight-long', accessories: 'none' },
  colors: { hair: '#254CE9' },
});

// Store the resolved parts, colors, and style version.
const saved = JSON.stringify(avatar.toJSON());

// Restore later using the matching style version.
const restored = fromJSON(croods, JSON.parse(saved));
const svg = restored.toString();
```

## Parts (the only valid IDs)

| Slot | Required | Default | Valid IDs |
| --- | --- | --- | --- |
| `head` | yes | `default` | `afro-1`, `afro-2`, `bald`, `bangs`, `bowl-cut`, `braid-1`, `braid-2`, `bun-1`, `bun-2`, `bun`, `default`, `dread`, `long-hair`, `long-hair-1`, `long-hair-2`, `messy-afro`, `messy`, `mohawk`, `no-hair`, `normal`, `pixie`, `quiff-1`, `quiff-2`, `shaggy`, `short-hair-1`, `short-hair-2`, `short-hair-3`, `short-hair-4`, `short-hair-5`, `short-hair-6`, `short-hair-7`, `short-hair-8`, `short-hair-9`, `straight-long`, `trimmed`, `wavy-curls` |
| `face` | no | `normal` | `drool`, `drool-2`, `happy`, `normal`, `open-mouth-1`, `sad`, `none` |
| `upperBody` | yes | `t-shirt-1` | `blazer`, `hoodie-1`, `shirt-1`, `t-shirt-1`, `t-shirt-bag` |
| `facialHair` | no | `none` | `beard`, `moustache`, `stubble`, `none` |
| `accessories` | no | `none` | `glass`, `none` |

Seeds pick every slot except `facialHair`, which stays `none` unless you select it.

Machine-readable: https://illlustrations.co/docs/parts.json

## Colors

Three layers, applied in this order:

1. `theme`: `'ink'` or `'neutral'` recolors everything.
2. `palette`: swap any artwork color by its hex, like the website builder.
3. `colors`: the four roles (`hair`, `skin`, `clothing`, `stroke`) always win.

### Color lists

Any role and `background` take a list; the seed picks one entry per person:

```tsx
// One color, or a list: the seed picks one entry per person, every time.
<Avatar
  assets={croods}
  seed={user.id}
  colors={{ clothing: ['#254CE9', '#F76549', '#77E87B'], hair: ['original', '#272727'] }}
  background={['#DDE5FF', '#FFE6D2', '#D4EDE2']}
/>
```

### Palette

```ts
import { createAvatar } from '@illlustrations/avatars';
import { croods } from '@illlustrations/avatars/croods';

croods.palette;          // every artwork color: ['#000000', '#FF0000', '#FFFFFF', …]

const avatar = createAvatar(croods, {
  selections: { upperBody: 't-shirt-bag' },
  colors: { hair: '#254CE9' },          // roles first
  palette: { '#FF4B33': '#254CE9' },    // then any artwork color: here, the bag
});

avatar.sourceColors();   // artwork colors in this avatar's parts
```

| Artwork color | What it paints |
| --- | --- |
| `#000000` | Outlines (stroke role) |
| `#FF0000` | Hair and beard (hair role) |
| `#FFFFFF` | Skin (skin role), eye whites and teeth |
| `#313130` | Dark garments (clothing role) |
| `#FF4B33` | Bag on t-shirt-bag |
| `#FFE900` | Yellow garments (clothing role), shirt under the blazer |
| `#FF3F3F` | Mouth and tongue |
| `#424242` | Mouth shadow |
| `#5DECFF` | Drool |
| `#F4F1EB` | Detail on shirt-1 |

A palette entry changes every use of that color, so change skin with `colors.skin`
rather than `#FFFFFF` (which also paints eye whites). Unknown colors throw.

## Options

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `assets` | AvatarStyle | Required | The imported style, such as croods. React prop; for createAvatar it is the first argument. |
| `seed` | string | Default character | Repeatable part selection. Same seed and style version give the same result; different seeds can share a result. Seeds never add facial hair. |
| `selections` | object | From seed | Override head, face, upperBody, facialHair or accessories. Optional slots accept 'none'. |
| `colors` | object | Original artwork | Override hair, skin, clothing or stroke. Hex, 'transparent' or 'original', or a list the seed picks from. |
| `theme` | 'ink' \| 'neutral' | Original artwork | 'ink' uses #0040FC and white; 'neutral' uses black and white. Explicit colors override theme roles. |
| `palette` | object | None | Swap any artwork color, like the builder: { '#FF4B33': '#254CE9' }. Keys come from croods.palette. Roles win over it; it wins over theme accents. |
| `background` | string \| string[] | 'transparent' | A hex color or 'transparent', or a list the seed picks from. Separate from the artwork palette. |
| `size` | number | 64 React / 600 core | Width and height in pixels. Above 0, at most 8192. |
| `shape` | string | 'square' | 'square', 'rounded' or 'circle'. |
| `seedPool` | string | 'v1' | The versioned list of seeded combinations. Only needed to pin results across versions. |
| `title` | string | Decorative | Accessible name (React). Also accepts ARIA, className, style and SVG presentation props. |

## Errors

| Error | Cause |
| --- | --- |
| `Unknown head part: curly-hair` | A part ID that isn't in the style. Use an ID from the parts list. |
| `Unknown slot: hair` | A selection key that isn't a slot. Slots are head, face, upperBody, facialHair and accessories. |
| `Invalid hair color: use a hex color, transparent or original` | Named CSS colors like 'red' are not accepted. |
| `Size must be greater than 0 and at most 8192` | Size out of range. |
| `idPrefix must start with a letter…` | toString({ idPrefix }) got an ID starting with a digit or containing other characters. |
| `Avatar state schema or style version does not match` | fromJSON got state saved with a different style version. |
| `Unknown palette color: #ABCDEF is not in the croods artwork` | A palette key that the artwork never uses. Pick keys from croods.palette or avatar.sourceColors(). |

## Recipes

### Profile picture fallback

Show the uploaded photo when there is one, and a stable avatar when there isn't.

```tsx
import { Avatar } from '@illlustrations/avatars/react';
import { croods } from '@illlustrations/avatars/croods';

export function UserPhoto({ user }: { user: { id: string; name: string; photoUrl?: string } }) {
  if (user.photoUrl) return <img src={user.photoUrl} alt={user.name} width={40} height={40} />;
  // Seed with the user ID, not the name: names change, IDs don't.
  return <Avatar assets={croods} seed={user.id} size={40} shape="circle" title={user.name} />;
}
```

### Member list

One theme and shape for a calm, consistent list. Decorative avatars need no title when the name is next to them.

```tsx
import { Avatar } from '@illlustrations/avatars/react';
import { croods } from '@illlustrations/avatars/croods';

export function Members({ members }: { members: { id: string; name: string }[] }) {
  return (
    <ul>
      {members.map(member => (
        <li key={member.id}>
          <Avatar assets={croods} seed={member.id} size={32} shape="circle" theme="neutral" background="#F2F0EE" />
          {member.name}
        </li>
      ))}
    </ul>
  );
}
```

### Let people choose their avatar

Build a picker from the style's part list and save the resolved state. A seed alone can't restore explicit choices.

```tsx
'use client';
import { useState } from 'react';
import { createAvatar, type AvatarState } from '@illlustrations/avatars';
import { Avatar } from '@illlustrations/avatars/react';
import { croods } from '@illlustrations/avatars/croods';

export function AvatarPicker({ userId, onSave }: { userId: string; onSave: (state: AvatarState) => void }) {
  const [head, setHead] = useState<string>();
  const options = { seed: userId, selections: head ? { head } : {} };
  return (
    <>
      <Avatar assets={croods} {...options} size={120} title="Your avatar" />
      <select value={head ?? ''} onChange={event => setHead(event.target.value || undefined)}>
        <option value="">Surprise me</option>
        {croods.parts.head.map(part => <option key={part.id} value={part.id}>{part.name ?? part.id}</option>)}
      </select>
      <button onClick={() => onSave(createAvatar(croods, options).toJSON())}>Save</button>
    </>
  );
}

// Later, render the saved state:
// <Avatar assets={croods} selections={saved.selections} colors={saved.colors}
//   theme={saved.theme} background={saved.background} shape={saved.shape} />
```

### An avatar URL (Next.js route)

Serve avatars as cacheable image URLs, for places that only take a src.

```tsx
import { createAvatar } from '@illlustrations/avatars';
import { croods } from '@illlustrations/avatars/croods';

export async function GET(_request: Request, { params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const svg = createAvatar(croods, { seed: id, size: 256, shape: 'circle' }).toString({ title: 'Avatar' });
  return new Response(svg, {
    headers: { 'Content-Type': 'image/svg+xml', 'Cache-Control': 'public, max-age=86400' },
  });
}
```

Same seed and style version always give the same image, so these URLs cache well.

### Server-rendered HTML

Several inline SVG strings on one page need unique ID prefixes, or their masks clash.

```tsx
import { createAvatar } from '@illlustrations/avatars';
import { croods } from '@illlustrations/avatars/croods';

// idPrefix: starts with a letter; letters, digits, _ and - only; unique on the page.
// Clean the ID and add the index, so odd characters or repeated users never clash.
const prefix = (id: string, index: number) => `avatar-${index}-${id.replace(/[^A-Za-z0-9_-]/g, '')}`;

const html = users.map((user, index) =>
  createAvatar(croods, { seed: user.id, size: 40 })
    .toString({ idPrefix: prefix(user.id, index), title: user.name })
).join('');
```

<Avatar> and toDataUri() handle IDs for you. Only inline toString() output needs idPrefix. Seed with the raw ID; only the prefix needs cleaning.

### PNG for email and social images

Many email clients and social cards don't render SVG. Convert on the server.

```tsx
import sharp from 'sharp';
import { createAvatar } from '@illlustrations/avatars';
import { croods } from '@illlustrations/avatars/croods';

const svg = createAvatar(croods, { seed: 'alex', size: 512, background: '#EDEDFF' }).toString();
const png = await sharp(Buffer.from(svg)).png().toBuffer();
```

## Attribution

```md
Croods v2 by [illlustrations / Vijay Verma](https://illlustrations.co),
© 2026, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
```
