@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-charactersThis 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, andlocalStorage - Pages must be served over HTTP or HTTPS (not
file://)
License
GPL-2.0-or-later
