Seed-Based Dithered SVG Avatar Generator for JS & React – dither-avatar
It helps you create repeatable user avatars for profiles, comments, dashboards, team lists, and account switchers when your app has names, emails, IDs, or usernames but no uploaded profile photos.
The package includes a React component for immediate use in component-based apps, and its core functions run in any JavaScript runtime like browsers, Node.js, and Deno.
Use npm for Vite, Next.js, Astro, React, or any bundled JavaScript project that supports ESM imports.
npm install dither-avatar
The package exposes core JavaScript functions from dither-avatar and React components from dither-avatar/react.
Use this example when you want full control over where the SVG markup appears in a vanilla JavaScript view.
import { generateDitherAvatar } from "dither-avatar";
const avatarMount = document.querySelector("#profile-avatar");
const accountSeed = "customer-7842";
if (avatarMount) {
// generateDitherAvatar returns a complete SVG string.
const avatarSvg = generateDitherAvatar(accountSeed);
// Insert the generated SVG into the target container.
avatarMount.innerHTML = avatarSvg;
// Mark the avatar as decorative when a visible username exists nearby.
avatarMount.setAttribute("aria-hidden", "true");
}
<div class="profile-row"> <div id="profile-avatar" class="profile-avatar"></div> <span class="profile-name">Morgan Lee</span> </div>
.profile-row {
display: flex;
align-items: center;
gap: 12px;
}
.profile-avatar svg {
width: 48px;
height: 48px;
border-radius: 50%;
}
Use a data URI when you want to place the generated avatar inside an <img> tag. This works well for lists, cards, tables, and other UI patterns that already expect image elements.
import { ditherAvatarDataUri } from "dither-avatar";
const teamMembers = [
{
accountId: "acct-1081",
name: "Riley Carter",
role: "Frontend Developer",
},
{
accountId: "acct-2094",
name: "Nora Patel",
role: "Product Manager",
},
{
accountId: "acct-3177",
name: "Evan Brooks",
role: "Support Lead",
},
];
const list = document.querySelector("#team-list");
if (list) {
teamMembers.forEach((member) => {
// Create a list item for each team member.
const item = document.createElement("li");
item.className = "team-card";
// Create a repeatable avatar from the account ID.
const avatar = new Image();
avatar.src = ditherAvatarDataUri(member.accountId);
avatar.width = 40;
avatar.height = 40;
avatar.alt = "";
avatar.className = "team-card-avatar";
// Use textContent for user visible strings.
const details = document.createElement("div");
details.className = "team-card-details";
const name = document.createElement("strong");
name.textContent = member.name;
const role = document.createElement("span");
role.textContent = member.role;
details.append(name, role);
item.append(avatar, details);
list.append(item);
});
}
<ul id="team-list" class="team-list"></ul>
.team-list {
display: grid;
gap: 10px;
padding: 0;
list-style: none;
}
.team-card {
display: flex;
align-items: center;
gap: 10px;
}
.team-card-avatar {
border-radius: 50%;
}
.team-card-details {
display: grid;
gap: 2px;
}
Use the React wrapper when your app already renders users through React components. The DitherAvatar component renders an <img> element and uses a generated data URI.
import { DitherAvatar } from "dither-avatar/react";
const workspaceUsers = [
{
id: "user-501",
name: "Avery Stone",
email: "avery@example.test",
},
{
id: "user-884",
name: "Jordan Miles",
email: "jordan@example.test",
},
];
export default function WorkspaceSwitcher() {
return (
<nav className="workspace-switcher" aria-label="Workspace users">
{workspaceUsers.map((user) => (
<button className="workspace-user" type="button" key={user.id}>
{/* Use a stable ID as the seed for repeatable avatar output. */}
<DitherAvatar
seed={user.id}
size={44}
className="workspace-user-avatar"
style={{ borderRadius: "12px" }}
/>
{/* Keep the readable user identity in normal text. */}
<span className="workspace-user-text">
<strong>{user.name}</strong>
<small>{user.email}</small>
</span>
</button>
))}
</nav>
);
}
.workspace-switcher {
display: grid;
gap: 8px;
}
.workspace-user {
display: flex;
align-items: center;
gap: 10px;
padding: 8px;
border: 1px solid #ddd;
background: #fff;
cursor: pointer;
}
.workspace-user-text {
display: grid;
text-align: left;
}
Use a small cache when a table or list may render the same user more than once. The library already generates compact output, but caching keeps repeated UI renders cleaner in dashboards and admin screens.
import { ditherAvatarDataUri } from "dither-avatar";
const avatarUriCache = new Map();
function getCachedAvatarUri(seed) {
// Reuse the URI when the same seed appears again.
if (avatarUriCache.has(seed)) {
return avatarUriCache.get(seed);
}
// Generate the data URI once per seed.
const uri = ditherAvatarDataUri(seed);
avatarUriCache.set(seed, uri);
return uri;
}
async function renderAssigneeTable() {
const tableBody = document.querySelector("#assignee-table-body");
if (!tableBody) {
return;
}
// Replace this endpoint with your application route.
const response = await fetch("/api/demo-assignees");
const assignees = await response.json();
tableBody.textContent = "";
assignees.forEach((assignee) => {
const row = document.createElement("tr");
const avatarCell = document.createElement("td");
const avatar = new Image();
// Use the database ID or email as the stable avatar seed.
avatar.src = getCachedAvatarUri(assignee.id);
avatar.width = 32;
avatar.height = 32;
avatar.alt = "";
avatarCell.append(avatar);
const nameCell = document.createElement("td");
nameCell.textContent = assignee.name;
const statusCell = document.createElement("td");
statusCell.textContent = assignee.status;
row.append(avatarCell, nameCell, statusCell);
tableBody.append(row);
});
}
renderAssigneeTable();
<table class="assignee-table">
<thead>
<tr>
<th>Avatar</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody id="assignee-table-body"></tbody>
</table>
Use DitherAvatarSVG when you need an inline SVG in a React component. The component renders SVG content through dangerouslySetInnerHTML, so the image based DitherAvatar component should remain the default choice for most user profile UIs.
import { DitherAvatarSVG } from "dither-avatar/react";
export function ProjectBadge({ projectId, projectName }) {
return (
<div className="project-badge">
{/* The inline SVG keeps the avatar in the React markup tree. */}
<DitherAvatarSVG
seed={projectId}
size={36}
className="project-badge-icon"
style={{ borderRadius: "8px" }}
/>
{/* The visible label carries the project name. */}
<span>{projectName}</span>
</div>
);
}
Use stable seed values. A database ID, username, email hash, or workspace slug works better than a display name that users can edit.
Prefer ditherAvatarDataUri() for <img> usage. It gives you a direct data:image/svg+xml,... value for src.
Prefer DitherAvatar in React user interfaces. It renders an image element and keeps generated SVG markup outside the React DOM tree.
Use DitherAvatarSVG only when inline SVG markup fits your styling or layout requirements. The component uses dangerouslySetInnerHTML, so treat it as a lower level rendering option.
Set alt="" for decorative avatars when the user name appears next to the image. Use a real text label elsewhere in the row or card.
seed (string): Sets the deterministic input for the generated avatar.size (number, optional): Sets the rendered width and height. The React components default to 40.className (string, optional): Adds a CSS class to the rendered image or SVG element.style (CSSProperties, optional): Adds inline React styles. The component sets borderRadius: "50%" first, and custom style values can override it.import {
generateColors,
generateDitherAvatar,
ditherAvatarDataUri,
GRID,
SIZE,
} from "dither-avatar";
// Returns matching fill and stroke colors for a seed.
const colors = generateColors("billing-admin-12");
// Returns raw SVG markup for a seed.
const svgMarkup = generateDitherAvatar("billing-admin-12");
// Returns a data URI for use in an img src attribute.
const imageSource = ditherAvatarDataUri("billing-admin-12");
// Exposes the generated SVG canvas size in pixels.
console.log(SIZE);
// Exposes the grid dimension used for the dither pattern.
console.log(GRID);
import { DitherAvatar, DitherAvatarSVG } from "dither-avatar/react";
// Renders an img element with a generated avatar data URI.
<DitherAvatar
seed="workspace-owner-42"
size={48}
className="account-avatar"
style={{ borderRadius: "10px" }}
/>;
// Renders an inline svg element with generated avatar markup.
<DitherAvatarSVG
seed="workspace-owner-42"
size={48}
className="account-avatar-svg"
style={{ borderRadius: "10px" }}
/>;
Q: Can I use this library with a CDN like jsDelivr or unpkg?
A: The library ships as an ES module. It does not include a UMD or IIFE bundle. You need a build tool or an environment that supports native ES module imports.
Q: Will the same seed always produce the same avatar?
A: Yes. The hash function is deterministic and the entire pipeline is pure computation. The same seed string produces identical SVG output on every call, in every environment, on every platform.
Q: Is it safe to use DitherAvatarSVG with user-supplied seeds?
A: The seed value is passed through a hash function and used only to derive numbers. It is never embedded into the SVG markup as a string. The XSS risk is minimal in typical use. Apply content-security policies at the application level when handling untrusted user input.
Q: How do I make the avatar circular?
A: The DitherAvatar React component applies borderRadius: "50%" by default. For vanilla JS usage, apply border-radius: 50% on the container element or the <img> tag via CSS.
Q: Does this library work in Node.js for server-side avatar generation?
A: Yes. generateDitherAvatar and ditherAvatarDataUri have no browser dependencies. Both functions run in Node.js and other server-side JS runtimes. You can generate avatar strings at request time and embed them directly in server-rendered HTML.
The post Seed-Based Dithered SVG Avatar Generator for JS & React – dither-avatar appeared first on CSS Script.
GTA 6 pre-orders were rumored to go live today, May 18, but it looks like…
LEGO Batman: Legacy of the Dark Knight sees you rise as the Dark Knight and…
Forza Horizon 6 developer Playground has confirmed the global release times for the hotly anticipated…
Parody, when done correctly, can be one of the sharpest, funniest ways to show your…
Cloudbass, a U.K.-based provider of remote sports and live event production services, specializing in IP-based…
The post Tower Family Foundation Passes $3.5 Million Milestone appeared first on TV News Check.
This website uses cookies.