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

@actionkit/rn-voice-actions

v0.1.1

Published

Voice-driven action engine for React Native. Define actions, match voice commands, and execute handlers with confidence scoring.

Downloads

38

Readme

@actionkit/rn-voice-actions

Voice-driven action engine for React Native. Define actions with phrase patterns, match voice input with confidence scoring, and execute handlers — all with a minimal, composable API.

Features

  • Action registry — declare actions with multiple trigger phrases
  • Smart matching — exact, substring, and token-based Jaccard similarity scoring
  • Confidence thresholds — filter matches by configurable minimum confidence
  • React bindings — provider + hook for session state and capture lifecycle
  • Pluggable input — swap voice providers or use a static stub for testing
  • Event system — subscribe to capturing, recognized, matched, no_match, executed, and error events
  • TypeScript-first — full type safety with generics for action context
  • Dual format — ships ESM and CommonJS

Installation

npm install @actionkit/rn-voice-actions

For voice input on React Native, also install:

npm install @react-native-community/voice

react and @react-native-community/voice are optional peer dependencies. The core engine works standalone without either.

Quick start

import { createActions } from "@actionkit/rn-voice-actions";
import { ActionsProvider, useActions } from "@actionkit/rn-voice-actions/react";
import { createReactNativeVoiceProvider } from "@actionkit/rn-voice-actions/adapters";
import Voice from "@react-native-community/voice";

// 1. Create an input provider
const input = createReactNativeVoiceProvider({ Voice, locale: "en-US" });

// 2. Define actions and create the engine
const engine = createActions({
  actions: [
    {
      id: "nav.home",
      phrases: ["go home", "home", "back to home"],
      handler: ({ router }) => router.navigate("Home"),
    },
    {
      id: "nav.settings",
      phrases: ["open settings", "settings"],
      handler: ({ router }) => router.navigate("Settings"),
    },
  ],
  threshold: 0.6,
  getContext: () => ({ router }),
  input,
});

// 3. Wrap your app
export default function App() {
  return (
    <ActionsProvider engine={engine}>
      <MicButton />
    </ActionsProvider>
  );
}

// 4. Use the hook
function MicButton() {
  const { state, startCapture, stopCapture } = useActions();

  return (
    <Pressable onPressIn={startCapture} onPressOut={stopCapture}>
      <Text>{state.status === "capturing" ? "Listening..." : "Hold to speak"}</Text>
    </Pressable>
  );
}

Package exports

| Import path | Description | |---|---| | @actionkit/rn-voice-actions | Core engine, types, resolver, and utilities | | @actionkit/rn-voice-actions/react | ActionsProvider and useActions hook | | @actionkit/rn-voice-actions/adapters | Input providers (React Native Voice, static stub) |

API

createActions(config)

Creates an action engine instance.

const engine = createActions({
  actions: Action[],          // required — array of action definitions
  threshold: number,          // optional — minimum confidence to match (default: 0.55)
  resolver: Resolver | "basic", // optional — custom resolver function (default: "basic")
  getContext: () => Context,  // optional — returns context passed to handlers
  input: InputProvider,       // optional — input source for voice capture
});

Returns: Actions object with resolve(), runAction(), on(), off(), emit().

Action

type Action<C> = {
  id: string;                        // unique action identifier
  phrases: string[];                 // trigger phrases to match against
  handler: (ctx: C) => void | Promise<void>; // executed on match
  enabled?: (ctx: C) => boolean;     // optional gate — skip when false
  meta?: {
    description?: string;
    category?: string;
  };
};

engine.resolve(input, context?)

Match an input string against registered actions. Returns a MatchResult:

// Successful match
{ type: "match", actionId: "nav.home", confidence: 1.0 }

// No match
{ type: "no_match", confidence: 0.3, candidates: [...] }

engine.runAction(match, context?)

Execute the handler for a matched action.

Built-in resolver scoring

| Match type | Confidence | |---|---| | Exact match | 1.0 | | Substring match | 0.8 | | Token Jaccard similarity | up to 0.7 |

Actions below the threshold are filtered out. You can replace the resolver entirely with a custom function.


React integration

ActionsProvider

Wraps your component tree and manages capture lifecycle.

<ActionsProvider
  engine={engine}       // required — engine from createActions()
  input={inputProvider}  // optional — override engine's input provider
  locale="en-US"        // optional — locale for speech recognition
  onEvent={(event, payload) => {}} // optional — callback for all events
>
  {children}
</ActionsProvider>

useActions()

Access the engine, session state, and capture controls.

const {
  engine,       // the action engine instance
  state,        // { status, input?, partial?, match?, error? }
  last,         // summary of last completed session
  startCapture, // begin listening
  stopCapture,  // stop listening and process input
  cancel,       // cancel current session
} = useActions();

state.status cycles through: idlecapturingprocessingexecutingidle


Adapters

createReactNativeVoiceProvider(options)

Wraps @react-native-community/voice as an InputProvider.

import { createReactNativeVoiceProvider } from "@actionkit/rn-voice-actions/adapters";
import Voice from "@react-native-community/voice";

const input = createReactNativeVoiceProvider({
  Voice,              // required — the Voice module
  locale: "en-US",    // optional — default locale
  onError: (err) => {}, // optional — error callback
});

createStaticInputProvider(input)

Returns a fixed string on every capture. Useful for testing.

import { createStaticInputProvider } from "@actionkit/rn-voice-actions/adapters";

const input = createStaticInputProvider("open settings");

Custom input provider

Implement the InputProvider interface to support any input source:

type InputProvider = {
  start: (opts?: { locale?: string }) => Promise<void>;
  stop: () => Promise<{ input: string }>;
  cancel?: () => Promise<void>;
  onPartial?: (cb: (partial: string) => void) => () => void;
};

Events

Subscribe to engine events directly or via the onEvent prop on ActionsProvider.

const unsub = engine.on("matched", ({ input, match, context }) => {
  console.log(`Matched "${input}" → ${match.actionId} (${match.confidence})`);
});

// Clean up
unsub();

| Event | Payload | |---|---| | capturing | { locale? } | | recognized | { input } | | matched | { input, match, context } | | no_match | { input, result, context } | | executed | { input, actionId, context } | | error | { error } |


Custom resolver

Replace the built-in resolver with your own matching logic:

const engine = createActions({
  actions,
  resolver: ({ input, actions, threshold, context }) => {
    // Your matching logic here
    // Return: { type: "match", actionId, confidence }
    //     or: { type: "no_match", confidence, candidates }
  },
});

Example project

A full Expo Router example with voice-controlled navigation is included in examples/voice-navigation.

# From repo root
npm install
npm run build

# Run the example
cd examples/voice-navigation
npm install
npx expo prebuild --platform ios
npx expo run:ios

License

MIT