@blobatar/react-native

中文文档
2.7.0发布于 2026/08/29MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 1.1万

React Native and Expo adapter for blobatar. Deterministic geometric avatars.

原始使用文档
文档目录9 节
  1. Installation
  2. Usage
  3. Props
  4. Two differences from the other adapters
  5. Morphing between expressions
  6. The idle layer
  7. Reduced motion
  8. Accessibility
  9. License

@blobatar/react-native

React Native and Expo adapter for blobatar. Deterministic geometric avatars.

Installation

bun add @blobatar/react-native blobatar
npx expo install react-native-svg     # or: bun add react-native-svg

react-native-svg is a peer dependency and is what actually draws. Every blobatar is <Path> and <Circle> elements, no gradients, filters or masks.

Expo needs nothing else. An Expo app is a React Native app and react-native-svg is the same library in both, so there is no @blobatar/expo package.

Usage

import { Blobatar } from "@blobatar/react-native";

function Avatar() {
  return <Blobatar name="username" size={48} />;
}

Props

PropTypeDescription
namestringRequired. Who the blobatar is for.
sizenumberRequired here. Width and height in points.
backgroundstring | falseBackground style.
palettePaletteColor overrides.
huenumberPin the hue.
tonenumberPin the tone.
normalizebooleanNormalize the name.
contrastbooleanEnforce the contrast guarantee.
titlestringAccessible label.
expressionExpressionPose to render.
animatebooleanAnimatedBlobatar only, from @blobatar/react-native/animated. Run the idle layer. Defaults to false.
traitsTraitOverridesOverride specific traits.

Anything else lands on the underlying <Svg>, and wins over what the props above derived.

Two differences from the other adapters

Both are the platform rather than the package.

size is required. On the web, omitting it lets CSS size the element and the viewBox scales to whatever the page decides. React Native has no such fallback, so an unsized blobatar is a blank square. Defaulting it here was the alternative, and an adapter that invents a default is an adapter that changes the picture. The core is the only place a default is written down.

Motion is three components, not a prop. Blobatar is still, and stays the cheapest thing this package can draw. MorphingBlobatar animates the change between expressions. AnimatedBlobatar adds the idle layer on top. Each tier is a separate export so a bundler drops the ones an app never names, and the size gate holds all three apart.

animate therefore means something narrower here than on the web, where it selects between "hover" and "always". There is no hover on a touch screen, so it is a boolean on AnimatedBlobatar and nothing else takes it. Passing it to the other two is a compile error naming this package, which is the cheapest place to learn the difference.

expression works in full. A static pose bakes into the geometry, which is why it survives here for the same reason it survives in the string API. Setting a new one cuts to it.

import { happy } from "blobatar/expression";

<Blobatar name="username" size={48} expression={happy} />;

Morphing between expressions

To animate the change from one expression to the next instead of cutting to it, use MorphingBlobatar. It takes exactly the same props.

import { MorphingBlobatar } from "@blobatar/react-native";
import { happy, sad } from "blobatar/expression";

<MorphingBlobatar name="username" size={48} expression={busy ? sad : happy} />;

It is 300ms adopting an expression and 400ms returning to idle, on the same curve the web uses, and it interrupts cleanly: setting a new expression part way through the last one starts from wherever the face actually is. Nothing animates on mount.

It is a second component rather than a morph prop, and the reason is bytes. The morph is about 1.1 kB gzipped, and a prop on one component is reachable from that component whether or not anybody passes it, so every app would carry it, including the grid of still avatars that is most of the usage. As a separate export a bundler drops all of it for an app that never names it.

The idle layer is still absent, and this is not a step toward it. What moves here is the pose, which is thirteen numbers on a state change you control. The idle layer is six keyframe loops gated on :hover, and :hover has no meaning on a touch screen.

The idle layer

AnimatedBlobatar adds the ambient motion on top of the morph: breathe, bob, blink, the glance, and the two loops that belong to an expression rather than to the ambient layer, thinking's seesaw and mad's tremor.

import { AnimatedBlobatar } from "@blobatar/react-native/animated";

// a profile header, one large avatar
<AnimatedBlobatar name="ada" size={120} animate />

// a grid: only what the list says is on screen
<AnimatedBlobatar name={u.id} size={44} animate={visible.has(u.id)} />

animate is yours to drive, and that is the platform's doing. On the web the idle layer is gated on :hover, which is both the aesthetic answer and the performance one. There is no hover on a touch screen; motion.css says so itself, pausing every loop under @media not ((hover: hover) and (pointer: fine)). So the only mode this platform has is the always-on one, and when becomes a question your app can answer and a component drawn into a scroll view cannot: screen focus, list viewability, a user setting.

It defaults to false, so an AnimatedBlobatar nobody has told to animate is a still blobatar, exactly. Turning it on or off ramps over 400ms rather than switching, which is the stylesheet's own transition.

It runs on the UI thread. The loops are Reanimated worklets, so a sidebar full of agents all animating at once does not put a React render per blobatar per frame on the JS thread. That is the case this is built for.

It is a separate entry point, @blobatar/react-native/animated, and that is what keeps the dependency optional:

npx expo install react-native-reanimated react-native-worklets

Use expo install rather than npm install if you are on Expo. Worklets ships a JavaScript half and a native half that must match exactly, and on Expo the native half is whatever your SDK compiled into the app binary, so a version npm resolves happily still throws Mismatch between JavaScript part and native part on the first render. Reanimated is a native module either way, so this needs a development build; Expo Go will not run it.

Only this subpath needs them. Blobatar and MorphingBlobatar come from the package root and link neither, so an app drawing still avatars in a list installs no native animation library at all.

The cost is real and worth stating: the loops now exist twice, once in blobatar/idle and once as worklets in the adapter, because a worklet cannot call an imported function. packages/harness runs both over a wide sweep and asserts they agree exactly, so a transcription error is a failing test rather than something you notice on a device. The pose composition is deliberately not duplicated: it stays in core and runs in JavaScript, since a morph is a one-shot 300ms transition rather than a loop.

The seeded timings are the same ones the stylesheet reads, so a blobatar breathes on the same offset on both platforms, and a grid of them is a crowd rather than a heartbeat.

Reduced motion

Pass animate={!reduceMotion}, or render Blobatar instead. This package imports no React Native module at all, so it cannot read the setting itself, and your app already can:

import { AccessibilityInfo } from "react-native";

const reduce = AccessibilityInfo.useReduceMotionEnabled();

<AnimatedBlobatar name="username" size={48} expression={mood} animate={!reduce} />;

// or, to drop the morph as well
const C = reduce ? Blobatar : MorphingBlobatar;
<C name="username" size={48} expression={mood} />;

Accessibility

react-native-svg has no <title>, so title becomes the accessibility label on the root <Svg>: accessible, accessibilityLabel and accessibilityRole="image". Without one, the tree is hidden from screen readers on both platforms, which is the same call aria-hidden makes on the web.

License

MIT

版本与发布记录

@blobatar/react-native 共发布 3 个版本,首次发布于 2026/08/24;过去 12 个月发布了 3 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.7.0latest2026/08/292 个月前
2.6.02026/08/292 个月前
2.5.02026/08/242 个月前

关于 @blobatar/react-native 的常见问题

@blobatar/react-native 是什么?

React Native and Expo adapter for blobatar. Deterministic geometric avatars.

如何安装 @blobatar/react-native?

在项目目录运行 npm install @blobatar/react-native,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:blobatar(2.x)、react(>=18)、react-native(>=0.76)、react-native-svg(>=15)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@blobatar/react-native 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@blobatar/react-native 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@blobatar/react-native 可以商用吗?

@blobatar/react-native 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@blobatar/react-native 的下载量有多少?

根据 npm 官方下载统计,@blobatar/react-native 在 2026/10/09 – 2026/11/07 这 30 天内被下载 10,701 次,日均约 357 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@blobatar/react-native 还在维护吗?

最新版本 2.7.0 发布于 2026/08/29(2 个月前);至今共发布 3 个版本,其中过去 12 个月发布了 3 个;首次发布于 2026/08/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。