Skip to main content

Type-safe achievement tracking for web apps

Add gamification to any web application with zero runtime dependencies. Auto-unlock, progress tracking, anti-cheat, and fine-grained React hooks.

Everything you need for achievement tracking

A tiny, framework-agnostic library with optional React bindings for seamless integration

Type-safe IDs

Achievement IDs are inferred from definitions. Typos become compile errors.

Zero dependencies

The core package ships nothing but your own code. No bloat.

Anti-cheat built-in

Stored data is integrity-checked on every load with pluggable hash adapters.

Auto-unlock

Progress-based achievements unlock automatically when thresholds are reached.

Pluggable storage

Swap localStorage, in-memory, or bring your own adapter. Framework-agnostic.

React-ready

Fine-grained hooks that only re-render components that need updates.

Quick start

Get up and running in minutes with React or vanilla JavaScript

1

Install the package

Choose the package that matches your stack:
npm install achievements-react
2

Define your achievements

Create a file to define your achievements with full type inference:
import { defineAchievements, createAchievements, localStorageAdapter } from 'achievements-react'

const definitions = defineAchievements([
  { id: 'first-visit', label: 'First Visit', description: 'Open the app.' },
  { id: 'click-frenzy', label: 'Click Frenzy', description: 'Click 50 times.', maxProgress: 50 },
])

export type AchievementId = typeof definitions[number]['id']

export const { engine, Provider, useAchievements, useIsUnlocked, useProgress } =
  createAchievements<AchievementId>({
    definitions,
    storage: localStorageAdapter('my-app'),
  })
3

Use in your app

For React apps, wrap your app with the Provider and use the hooks:
<Provider>
  <App />
</Provider>

// Inside your components
function MyComponent() {
  const { unlock, incrementProgress } = useAchievements()
  const visited = useIsUnlocked('first-visit')
  const { progress, max } = useProgress('click-frenzy')

  return <button onClick={() => incrementProgress('click-frenzy')}>
    Click me! ({progress}/{max})
  </button>
}
For vanilla JS, use the engine directly:
engine.unlock('first-visit')
engine.subscribe((state) => {
  console.log('Unlocked:', state.unlockedIds)
})

Explore the documentation

Dive deeper into core concepts, guides, and API reference

Core concepts

Learn about achievements, progress tracking, anti-cheat, and storage adapters

React integration

Set up the Provider, use hooks, and integrate with your React app

Vanilla JS guide

Use the framework-agnostic core in any JavaScript environment

API reference

Complete API documentation for both core and React packages

Ready to add achievements to your app?

Start tracking achievements with type-safe IDs, auto-unlock, and anti-cheat in minutes

Build docs developers (and LLMs) love