# @illlustrations/avatars
Seeded SVG avatars for JavaScript and React. Everything renders locally: no API
key, network request or account. Croods is the first style. Version 1.1.0.
Web: https://illlustrations.co/docs · Parts JSON: https://illlustrations.co/docs/parts.json · Agent skill: https://illlustrations.co/docs/skill.md
## Install
```sh
npm install @illlustrations/avatars
```
React projects need React 18.3.1 or 19.
| 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. |
## React
```tsx
import { Avatar } from '@illlustrations/avatars/react';
import { croods } from '@illlustrations/avatars/croods';
export function UserAvatar() {
return (
);
}
```
Use `title` or an accessible label for meaningful avatars; otherwise the
component is decorative. The React entry declares `'use client'` and uses
`useId` for unique SVG IDs, so it works in Next.js pages. Apps with several
React roots need matching server/client `identifierPrefix` values.
## Plain JavaScript
```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);
```
For server output use `avatar.toString()`. Multiple inline SVG strings on one
page each need a unique `toString({ idPrefix })`.
## Seeds and randomness
```tsx
// Same seed, same avatar: on the server, the client and every device.
// Seeds pick head, face, outfit and glasses. Facial hair is opt-in.
// Explicit selections always win over the seed.
```
A seed picks one combination from a frozen, versioned pool (`v1`), so the same
seed and style version always give the same avatar. Different seeds can share
an avatar. Seeds never add facial hair; select it explicitly. Resolution order:
style defaults → seeded combination → explicit `selections`.
## Parts
| 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.
Read them at runtime:
```ts
import { croods } from '@illlustrations/avatars/croods';
const hairstyles = croods.parts.head.map(({ id, name }) => ({
value: id,
label: name ?? id,
}));
```
## 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.
```
### 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.
## API reference
| 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. |
`createAvatar(style, options)` returns `{ toString(options?), toDataUri(options?), toJSON() }`.
`toString` accepts `{ idPrefix?, title? }`; `toDataUri` accepts `{ title? }`.
`fromJSON(style, state)` validates and restores saved state.
### Errors
Invalid input throws instead of rendering something unexpected.
| 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(). |
## Save and restore
```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();
```
Saved state includes the style and schema versions. Mismatches are rejected
rather than silently changing the avatar.
### Static SVG files
The package includes 24 portraits, `croods-001.svg` to `croods-024.svg`:
```ts
import avatarUrl from '@illlustrations/avatars/svg/croods-001.svg?url';
const image = document.createElement('img');
image.src = avatarUrl;
image.alt = 'Croods avatar';
document.body.append(image);
```
`?url` is Vite-specific. In Next.js, copy the SVG to `public/` and use its URL.
## 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 ;
// Seed with the user ID, not the name: names change, IDs don't.
return ;
}
```
### 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 (