Register Home Screen actions and receive their identifiers
One.QuickActions registers dynamic actions in the Home Screen menu
shown on a long press of your app icon. Each action has a unique id, a
title, and an optional subtitle. Selections arrive as plain identifier
strings, whether they cold-start the app or land while it runs.
On iOS items become UIApplicationShortcutItem entries. On Android
they become dynamic launcher shortcuts through ShortcutManager. On
web validation still runs, but setting does nothing, reads come back
empty, and listeners never fire.
import { One } from 'one'
await One.QuickActions.setItems([ { id: 'open-inbox', title: 'Inbox', subtitle: 'View recent messages' },])
function openInbox() {}
function handleAction(id: string) { if (id === 'open-inbox') openInbox()}
const remove = One.QuickActions.addListener(handleAction)
const initial = One.QuickActions.getInitialAction()if (initial) handleAction(initial)One.QuickActions.clearInitialAction()setItems replaces the dynamic actions; an empty array removes them.
Handle both delivery paths: the listener for warm selections, the
initial action for cold starts.
Static actions declared in the app’s Info.plist are separate: iOS shows those first and may leave fewer menu positions for dynamic items. The system decides how many actions fit. The id delivered to JavaScript is the action’s native type string, including for a static action chosen from the Home Screen.
getInitialAction returns the identifier that cold-started the current
process, or null. Reading it never clears it; call
clearInitialAction after handling it. If several cold actions arrive
before clearing, the latest wins. addListener receives selections
made while the process runs and returns a remover; it never replays the
initial action. Warm selections with no registered listener are
dropped, so register early, near the app root, when navigation must
respond while suspended. A selected dynamic action stays in the menu
after process death until a later setItems replaces or clears it.
Items need unique, non-empty string ids and titles, plus optional
string subtitles; JavaScript rejects bad input with a TypeError. On
Android more items than the system maximum rejects instead of trimming,
and below API 25 shortcuts do not exist, so reads return empty. App
Intents and Siri shortcuts are separate APIs, not provided here.
| Methods | Type | Description |
|---|---|---|
setItems(items: QuickActionItem[]) => Promise<void>Replace the dynamic actions. Empty removes them. | (items: QuickActionItem[]) => Promise<void> | Replace the dynamic actions. Empty removes them. |
getItems() => Promise<QuickActionItem[]>The dynamic items registered right now. | () => Promise<QuickActionItem[]> | The dynamic items registered right now. |
getInitialAction() => string | nullThe id that cold-started this process. | () => string | null | The id that cold-started this process. |
clearInitialAction() => voidClear the cold-start id after handling. | () => void | Clear the cold-start id after handling. |
addListener(listener: (id: string) => void) => () => voidHear warm selections. Returns a remover. | (listener: (id: string) => void) => () => void | Hear warm selections. Returns a remover. |
| Item | Type | Description |
|---|---|---|
idstringUnique, non-empty. Delivered on selection. | string | Unique, non-empty. Delivered on selection. |
titlestringShown in the menu. | string | Shown in the menu. |
subtitlestringShown under the title. Optional. | string | Shown under the title. Optional. |
| iOS | Android | Web | |
|---|---|---|---|
| Items | Shortcut items | Dynamic cuts | None |
| Static first | Yes | Per launcher | None |
| Cold start | Initial id | Initial id | Null |
| Floor | All versions | API 25 plus | None |
import { useEffect } from 'react'import { One } from 'one'
function useQuickActionRoute(openInbox: () => void, openCompose: () => void) { useEffect(() => { function route(id: string) { if (id === 'open-inbox') openInbox() if (id === 'open-compose') openCompose() } const initial = One.QuickActions.getInitialAction() if (initial) route(initial) One.QuickActions.clearInitialAction() return One.QuickActions.addListener(route) }, [openInbox, openCompose])}Mounting near the root keeps the listener alive for the whole process. The initial action routes before the listener attaches, so cold starts land on the right screen on the first frame.
import { One } from 'one'
async function syncActions(signedIn: boolean) { if (!signedIn) { await One.QuickActions.setItems([]) return } await One.QuickActions.setItems([ { id: 'open-inbox', title: 'Inbox' }, { id: 'open-compose', title: 'New message' }, ])}Replacing the list on every auth change keeps signed-out users from seeing signed-in shortcuts. The empty write clears the menu entirely.
Edit this page on GitHub.