Factory that creates the achievement engine and returns hooks already bound to your achievement ID type. This eliminates the need to write useHook<AchievementId>() everywhere - all hooks are pre-typed.
Signature
function createAchievements < TId extends string >(
config : AchievementsConfig < TId >
) : {
engine : AchievementEngine < TId >;
Provider : ({ children } : { children : ReactNode }) => JSX . Element ;
useAchievements : () => AchievementEngine < TId >;
useIsUnlocked : ( id : TId ) => boolean ;
useProgress : ( id : TId ) => { progress : number ; max : number | undefined };
useAchievementToast : () => { queue : ReadonlyArray < TId >; dismiss : ( id : TId ) => void };
useUnlockedCount : () => number ;
useTamperDetected : () => string | null ;
}
Parameters
config
AchievementsConfig<TId>
required
Configuration object for the achievement system. definitions
AchievementDef<TId>[]
required
Array of achievement definitions created with defineAchievements.
storage
StorageAdapter
default: "inMemoryAdapter()"
Storage adapter for persisting achievement state (e.g., localStorageAdapter, inMemoryAdapter).
hash
HashAdapter
default: "fnv1aHashAdapter"
Hash adapter for tamper detection.
Callback invoked when tampered storage is detected.
Returns
The core achievement engine instance for imperative API calls.
Provider
({ children }: { children: ReactNode }) => JSX.Element
Drop-in React context provider component with the engine already bound - no engine prop needed.
useAchievements
() => AchievementEngine<TId>
Hook that returns the engine for imperative calls (unlock, setProgress, etc.).
Reactive hook that returns whether a specific achievement is unlocked. Re-renders only when that achievement’s lock state changes.
useProgress
(id: TId) => { progress: number; max: number | undefined }
Reactive hook that returns the current progress and max progress for an achievement. Re-renders only when that achievement’s progress changes.
useAchievementToast
() => { queue: ReadonlyArray<TId>; dismiss: (id: TId) => void }
Reactive hook for toast notifications with queue and dismiss helper.
Reactive hook that returns the total count of unlocked achievements. Re-renders only when the count changes.
Returns the storage key that failed its integrity check, or null if none. Handles both pre-mount and runtime tamper detection.
Example
// achievements.ts
import {
defineAchievements ,
createAchievements ,
localStorageAdapter ,
} from "achievements-react" ;
export const definitions = defineAchievements ([
{
id: "first-visit" ,
label: "First Contact" ,
description: "Initialized session for the first time." ,
},
{
id: "click-frenzy" ,
label: "Input Overflow" ,
description: "Registered 50 consecutive inputs." ,
maxProgress: 50 ,
},
]);
export type AchievementId = ( typeof definitions )[ number ][ "id" ];
export const {
engine ,
Provider ,
useAchievements ,
useIsUnlocked ,
useProgress ,
useAchievementToast ,
useUnlockedCount ,
useTamperDetected ,
} = createAchievements < AchievementId >({
definitions ,
storage: localStorageAdapter ( "my-app" ),
});
// App.tsx
import { Provider , useTamperDetected } from "./achievements" ;
import { Dashboard } from "./Dashboard" ;
export default function App () {
const tamperKey = useTamperDetected ();
if ( tamperKey !== null ) {
return < div > Tamper detected on key: { tamperKey } </ div > ;
}
return (
< Provider >
< Dashboard />
</ Provider >
);
}
// Component.tsx
import { useIsUnlocked } from "./achievements" ;
function MyComponent () {
// Fully typed - no <T> needed!
const unlocked = useIsUnlocked ( "click-frenzy" );
return < div > { unlocked ? "Unlocked!" : "Locked" } </ div > ;
}
Notes
All returned hooks are already bound to your TId type, eliminating the need for generic type parameters in components
The Provider component has the engine pre-bound, so you don’t need to pass an engine prop
The factory handles tamper detection buffering, ensuring useTamperDetected() works even when tampering is detected before React mounts
You can still access the engine directly for imperative operations or use it outside of React components