A form error
Submit an invalid email. It winces and turns to look at the field.
→ wince, then cheer when it's valid
pagepal reacts to what's happening on your page: form errors, slow requests, buttons that do nothing, going offline. The same signals become UX analytics that you own.
npm install pagepalThe pal in the bottom corner is watching this page. Try these and watch it react. Right now it's neutral
Submit an invalid email. It winces and turns to look at the field.
→ wince, then cheer when it's valid
Requests over 150 ms show the pal waiting. Failures make it wince.
→ waiting, wince
It covers its eyes while you type, and peeks when you show the password.
→ shy
Click once and nothing happens: a dead click. Click it four times fast: a rage click.
→ confused, concerned
Pretend to go offline (or really do, in DevTools). It's sad until you're back.
→ sad, then cheer
Scroll fast to make it dizzy. Leave it alone for 30 seconds and it dozes off. Poke it, drag it anywhere, or hover and press ✕.
→ dizzy, dozing, wink
When several things happen at once, the most important one wins. A form error while offline shows a wince, then goes back to sad.
The signals that move the mascot are the ones you'd want in a dashboard. Add one plugin and pagepal sends them to your endpoint, or to the analytics tool you already use.
import { createPal } from 'pagepal';
import { analytics } from 'pagepal/analytics';
createPal({
watch: 'form#checkout',
requests: true,
plugins: [analytics({ endpoint: '/api/ux' })],
});button#apply-coupon "Apply"button#pay "Pay $##"div.plan-card "Enterprise"input[name="zip"]input[name="email"]form#checkout: 20 submitted, 10 abandoned, most often on input[name="zip"]
GET /api/shipping/ratesPOST /api/paymentsSample data: a made-up week of checkout traffic, run through summarize().
Steps aside for chat widgets, cookie banners and the field you're typing in. Users can drag it anywhere or dismiss it, and it remembers.
The artwork is hidden from screen readers, the dismiss button is a real labelled button, and with reduced motion it changes expression without animating.
Nothing is sent unless you configure it. No input values, digits and emails masked, IDs stripped from URLs, consent-aware.
No dependencies. Build from pagepal/core and pick only the watchers you need. Size budgets are checked in CI.
Rendered in a Shadow DOM: your CSS can't break it and its CSS can't leak into your page.
Plugins raise their own signals, a debug panel explains every mood, and you can draw your own character.
Pick a built-in character, draw your own in a few lines of SVG and get every expression for free, or bring your own images for each mood.
Characters & colorA web component for plain HTML and every framework, plus first-class React and Vue components. Safe to import during server-side rendering.
<script src="https://unpkg.com/pagepal"></script>
<page-pal watch="form#signup" requests></page-pal>import { createPal } from 'pagepal';
createPal({ watch: 'form#signup', requests: true });import { PagePal } from 'pagepal/react';
export function App() {
return (
<>
<PagePal watch="form#signup" requests character="cat" />
<Routes />
</>
);
}<script setup>
import { PagePal } from 'pagepal/vue';
</script>
<template>
<PagePal watch="form#signup" requests :options="{ character: 'ghost' }" />
</template>Five minutes to add. Useful from the first form error.