npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nanonomad/react

v1.0.3

Published

React / Next.js client wrapper for NanoNomad core (SSR-safe mount)

Readme

@nanonomad/react

A React wrapper for the NanoNomad digital pet engine. Provides a single <NanoNomadPet> component that mounts and manages the pet's lifecycle automatically. Compatible with React 18+, Next.js (App Router and Pages Router), and any SSR-aware React framework.


Installation

npm install @nanonomad/react

@nanonomad/core and @nanonomad/characters are installed automatically. You can also import helpers from this package: import { getCharacterUrls } from '@nanonomad/react'.


Setup: copy character assets

Before using the component, copy the character files into your project's public folder. The engine fetches these files at runtime via fetch().

npx nanonomad-characters-copy ./public/nanonomad-characters

This creates:

public/
  nanonomad-characters/
    pet.css
    default/
      character.json
      default.css
      default-front.svg
      ...
    dog/
      character.json
      dog.css
      dog-front.svg
      ...

Add this as a project script so assets stay current after package updates:

{
  "scripts": {
    "sync:pet": "nanonomad-characters-copy ./public/nanonomad-characters"
  }
}

Basic usage

Mount the component once near the root of your application. It renders a fixed full-screen overlay and should not be nested inside a constrained container.

import '@nanonomad/characters/assets/pet.css'
import '@nanonomad/characters/assets/default/default.css'
import NanoNomadPet from '@nanonomad/react'

const config = {
  character: 'default',
  characterBaseUrl: `${window.location.origin}/nanonomad-characters/default/`,
  characterConfigUrl: `${window.location.origin}/nanonomad-characters/default/character.json`,
  movementSpeed: 50,
  behaviorFrequency: 50,
  mobileEnabled: false,
  persistenceEnabled: true,
  enabledCapabilities: ['idle', 'walk', 'jump', 'sit', 'fall'],
}

export default function App() {
  return (
    <>
      <NanoNomadPet runtimeConfig={config} />
      {/* rest of your app */}
    </>
  )
}

Import pet.css and the character's CSS once per application — not per component render.


Props

runtimeConfig

Type: object — Required

The configuration object passed directly to the NanoNomad engine. All parameters from @nanonomad/core are supported.

| Parameter | Type | Default | Description | |---|---|---|---| | character | string | 'default' | The character to display. Must match a folder in your public characters directory. Available built-in values: 'default' (Cat), 'dog' (Doggo). | | characterBaseUrl | string | '' | Base URL for the character's asset folder. The engine fetches SVG and CSS files relative to this path. Must end with /. | | characterConfigUrl | string | '' | Full URL to the character's character.json. | | movementSpeed | number | 50 | Movement speed from 1 to 100. Higher values produce faster walking and running. | | behaviorFrequency | number | 50 | How actively the pet transitions between states, from 1 to 100. Higher values produce more restless behavior. | | mobileEnabled | boolean | false | Set to true to enable the pet on screens narrower than 768 px. | | persistenceEnabled | boolean | true | Saves and restores the pet's position across page loads using localStorage. | | enabledCapabilities | string[] | [] | Capabilities the pet may use. An empty array enables everything the character supports. Pass a subset to restrict behavior. Valid values: 'idle', 'walk', 'run', 'jump', 'sit', 'sleep', 'pee', 'fall'. | | customColors | object | {} | Override the character's default colors. Keys must match the character's colorPalette entries in character.json. | | characterProvider | object | null | Advanced: supply a custom character loader with a getCharacter(id) async method. See the @nanonomad/core documentation. |

registerBuiltin

Type: boolean — Default: true

When true, the engine loads all built-in capabilities (idle, walk, run, jump, sit, sleep, pee, fall) automatically. Set to false only if you are supplying a fully custom capability set.


Configuration updates

The component re-initializes the engine whenever runtimeConfig changes by deep value (via JSON.stringify comparison). To avoid unintentional restarts, define the config object outside the component or memoize it.

// Define outside the component — config is stable across renders
const config = {
  character: 'dog',
  characterBaseUrl: '/nanonomad-characters/dog/',
  characterConfigUrl: '/nanonomad-characters/dog/character.json',
  movementSpeed: 70,
  enabledCapabilities: ['idle', 'walk', 'run', 'jump', 'sit', 'sleep', 'fall'],
}

export default function App() {
  return <NanoNomadPet runtimeConfig={config} />
}

Or memoize inside the component when the config depends on state:

import { useMemo, useState } from 'react'
import NanoNomadPet from '@nanonomad/react'

export default function App() {
  const [speed, setSpeed] = useState(50)

  const config = useMemo(() => ({
    character: 'default',
    characterBaseUrl: '/nanonomad-characters/default/',
    characterConfigUrl: '/nanonomad-characters/default/character.json',
    movementSpeed: speed,
  }), [speed])

  return (
    <>
      <NanoNomadPet runtimeConfig={config} />
      <input type="range" value={speed} onChange={e => setSpeed(Number(e.target.value))} />
    </>
  )
}

Color customization

Pass customColors with keys matching the character's color palette to override default colors.

// Cat (default) palette keys: body, head, legs, blush, eyes, detail
const config = {
  character: 'default',
  characterBaseUrl: '/nanonomad-characters/default/',
  characterConfigUrl: '/nanonomad-characters/default/character.json',
  customColors: {
    body: '#7c3aed',
    head: '#6d28d9',
    legs: '#5b21b6',
  },
}

// Dog palette keys: body, belly, ears, detail, eyes, tongue
const config = {
  character: 'dog',
  characterBaseUrl: '/nanonomad-characters/dog/',
  characterConfigUrl: '/nanonomad-characters/dog/character.json',
  customColors: {
    body: '#92400e',
    tongue: '#f43f5e',
  },
}

Using the CDN instead of self-hosted assets

If you prefer not to copy files into your project, use the jsDelivr CDN via the URL helpers from @nanonomad/characters. The CDN mirrors npm automatically after each release.

import NanoNomadPet from '@nanonomad/react'
import { getCharacterUrls } from '@nanonomad/characters'

const VERSION = '1.0.3'
const characterId = 'default'

const config = {
  ...getCharacterUrls(characterId, { version: VERSION }),
  movementSpeed: 50,
  behaviorFrequency: 50,
}

export default function App() {
  return <NanoNomadPet runtimeConfig={config} />
}

Load the stylesheets via <link> tags in your HTML head when using the CDN rather than importing them as modules:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@nanonomad/[email protected]/assets/pet.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@nanonomad/[email protected]/assets/default/default.css">

Next.js

App Router

The component uses useEffect and useRef, which require a Client Component boundary. Add 'use client' to any file that renders <NanoNomadPet>.

'use client'

import '@nanonomad/characters/assets/pet.css'
import '@nanonomad/characters/assets/default/default.css'
import NanoNomadPet from '@nanonomad/react'

const config = {
  character: 'default',
  characterBaseUrl: '/nanonomad-characters/default/',
  characterConfigUrl: '/nanonomad-characters/default/character.json',
  movementSpeed: 50,
}

export default function PetWrapper() {
  return <NanoNomadPet runtimeConfig={config} />
}

Use path-based URLs rather than window.location.origin for characterBaseUrl, since the path is stable across server and client rendering contexts.

Import this wrapper in your root layout:

// app/layout.jsx
import PetWrapper from '@/components/PetWrapper'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <PetWrapper />
      </body>
    </html>
  )
}

Pages Router

Mount the pet in _app.jsx so it persists across page navigations:

// pages/_app.jsx
import '@nanonomad/characters/assets/pet.css'
import '@nanonomad/characters/assets/default/default.css'
import NanoNomadPet from '@nanonomad/react'

const config = {
  character: 'default',
  characterBaseUrl: '/nanonomad-characters/default/',
  characterConfigUrl: '/nanonomad-characters/default/character.json',
}

export default function App({ Component, pageProps }) {
  return (
    <>
      <Component {...pageProps} />
      <NanoNomadPet runtimeConfig={config} />
    </>
  )
}

React StrictMode

In development, React StrictMode intentionally mounts components twice to surface side effects. This will cause two pets to appear briefly during development. This is expected and does not occur in production builds.

To avoid this in development, mount <NanoNomadPet> outside <StrictMode>:

// main.jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import NanoNomadPet from '@nanonomad/react'
import App from './App'

createRoot(document.getElementById('root')).render(
  <>
    <StrictMode>
      <App />
    </StrictMode>
    <NanoNomadPet runtimeConfig={config} />
  </>
)

Limiting capabilities per character

The enabledCapabilities list filters which behaviors the pet will actually perform. Capabilities in this list still need to be supported by the character — you cannot enable sleep on the default Cat character because it does not define a sleep pose.

// Show only walking and idling — no jumping, sitting, or other states
const config = {
  character: 'default',
  characterBaseUrl: '/nanonomad-characters/default/',
  characterConfigUrl: '/nanonomad-characters/default/character.json',
  enabledCapabilities: ['idle', 'walk'],
}

// Dog with full behavior set
const config = {
  character: 'dog',
  characterBaseUrl: '/nanonomad-characters/dog/',
  characterConfigUrl: '/nanonomad-characters/dog/character.json',
  enabledCapabilities: ['idle', 'walk', 'run', 'jump', 'sit', 'sleep', 'pee', 'fall'],
}

idle and fall are always active regardless of what enabledCapabilities contains.


Capability reference by character

| Capability | Cat (default) | Dog | |---|---|---| | idle | Yes | Yes | | walk | Yes | Yes | | run | No | Yes | | jump | Yes | Yes | | sit | Yes | Yes | | sleep | No | Yes | | pee | No | Yes | | fall | Yes | Yes |


Peer dependencies

| Package | Version | |---|---| | react | 18.0.0 or later | | react-dom | 18.0.0 or later |


Requirements

  • Node.js 18 or later
  • Modern browser with fetch, requestAnimationFrame, and localStorage
  • Pages must be served over HTTP or HTTPS (not file://)

License

GPL-2.0-or-later