One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Quick Actions

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.

Usage

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.

How it works

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.

API reference

MethodsTypeDescription
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 | nullThe id that cold-started this process.
clearInitialAction() => voidClear the cold-start id after handling.
() => voidClear the cold-start id after handling.
addListener(listener: (id: string) => void) => () => voidHear warm selections. Returns a remover.
(listener: (id: string) => void) => () => voidHear warm selections. Returns a remover.
ItemTypeDescription
idstringUnique, non-empty. Delivered on selection.
stringUnique, non-empty. Delivered on selection.
titlestringShown in the menu.
stringShown in the menu.
subtitlestringShown under the title. Optional.
stringShown under the title. Optional.

Platform differences

iOSAndroidWeb
ItemsShortcut itemsDynamic cutsNone
Static firstYesPer launcherNone
Cold startInitial idInitial idNull
FloorAll versionsAPI 25 plusNone

Limits and gotchas

Examples

Route from the app root

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.

Refresh actions on sign-in

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.