Skip to main content

@lerx/promise-modal

Promise-based modal utility for React — alert, confirm, and prompt as async functions, usable inside and outside components.

@lerx/promise-modal npm versionlicense
yarn add @lerx/promise-modal

@lerx/promise-modal turns modal interactions into awaitable promises. Call alert, confirm, or prompt from anywhere — event handlers, async functions, or outside the React tree — and await the user's response.

  • Works inside and outside React components
  • Supports alert, confirm, and prompt modal types
  • Fully customizable component slots (foreground, background, title, footer, etc.)
  • Per-modal and global configuration
  • Component-lifecycle-scoped modals via useModal

Live Demo

Click the buttons below to try alert, confirm, and prompt — each returns a Promise that resolves with the user's response:

Requirements

  • React 18 or 19
  • Node.js 16.11.0+

Setup

1. Wrap your app with ModalProvider

import { ModalProvider } from '@lerx/promise-modal';

function App() {
return (
<ModalProvider>
<YourApp />
</ModalProvider>
);
}

2. Call modal functions anywhere

import { alert, confirm, prompt } from '@lerx/promise-modal';

// In an event handler, async function, or outside React:
async function handleDelete() {
const ok = await confirm({
title: 'Delete item',
content: 'This cannot be undone.',
footer: { confirm: 'Delete', cancel: 'Cancel' },
});
if (ok) await deleteItem();
}

3. Use useModal inside components for lifecycle-scoped modals

import { useModal } from '@lerx/promise-modal';

function MyComponent() {
const modal = useModal();
// Modals opened via `modal.*` are closed automatically when MyComponent unmounts.
return <button onClick={() => modal.alert({ title: 'Hi' })}>Open</button>;
}

ModalProvider with custom components and options

import { ModalProvider } from '@lerx/promise-modal';
import { CustomBackground, CustomForeground, CustomFooter } from './components';

function App() {
return (
<ModalProvider
ForegroundComponent={CustomForeground}
BackgroundComponent={CustomBackground}
FooterComponent={CustomFooter}
options={{
duration: 250, // animation duration in ms
backdrop: 'rgba(0,0,0,0.35)', // backdrop CSS color
manualDestroy: false, // auto-destroy on confirm/close
closeOnBackdropClick: true, // close when backdrop clicked
zIndex: 1000, // z-index for modal stack
}}
context={{ theme: 'light', locale: 'en-US' }}
>
<YourApp />
</ModalProvider>
);
}

Claude Code Integration

@lerx/promise-modal ships Claude Code assets — the promise-modal-skill expert skill with its API, hooks, and advanced-pattern knowledge base — under docs/claude/. They are published alongside the package so AI assistants can reason about modal flows with package-specific context.

Inject those assets into your local Claude Code environment with @slats/claude-assets-sync (bin: inject-claude-settings):

npx -p @slats/claude-assets-sync inject-claude-settings --package=@lerx/promise-modal --scope=user # inject into ~/.claude (user-level)
npx -p @slats/claude-assets-sync inject-claude-settings --package=@lerx/promise-modal --scope=project # inject into the nearest existing .claude, walking up from cwd

# Preview / overwrite flags
npx -p @slats/claude-assets-sync inject-claude-settings --package=@lerx/promise-modal --scope=user --dry-run # show the plan without writing
npx -p @slats/claude-assets-sync inject-claude-settings --package=@lerx/promise-modal --scope=user --force # overwrite locally-modified files

The CLI resolves @lerx/promise-modal/package.json via createRequire, reads its claude.assetPath (docs/claude), and applies hash-based diff into the selected .claude/ (skip unchanged, warn on divergence, require --force to overwrite).

AI Agent Reference

AI Reference

Package: @lerx/promise-modal v0.10.5 Purpose: Promise-based modal management for React — alert, confirm, prompt as async functions.

Core functions (usable anywhere, including outside React)

import { alert, confirm, prompt } from '@lerx/promise-modal';

Provider

import { ModalProvider } from '@lerx/promise-modal';
import type { ModalProviderHandle, ModalProviderProps } from '@lerx/promise-modal';

Hooks (inside React components)

import { useModal } from '@lerx/promise-modal'; // lifecycle-scoped modals
import { useInitializeModal } from '@lerx/promise-modal'; // provider initialization
import { useActiveModalCount } from '@lerx/promise-modal'; // open modal count
import { useModalAnimation } from '@lerx/promise-modal'; // animation state
import { useModalDuration } from '@lerx/promise-modal'; // animation duration
import { useModalOptions } from '@lerx/promise-modal'; // current options
import { useModalBackdrop } from '@lerx/promise-modal'; // backdrop state
import { useDestroyAfter } from '@lerx/promise-modal'; // auto-destroy timer
import { useSubscribeModal } from '@lerx/promise-modal'; // modal event subscription

Types

import type { ModalOptions } from '@lerx/promise-modal';
import type { ModalFrameProps, ModalBackground } from '@lerx/promise-modal';
import type { FooterComponentProps, PromptInputProps } from '@lerx/promise-modal';
import type { AlertContentProps, ConfirmContentProps, PromptContentProps } from '@lerx/promise-modal';
import type { WrapperComponentProps } from '@lerx/promise-modal';

Architecture

  • ModalProvider wraps app → enables global modal rendering
  • alert/confirm/prompt → returns Promise<boolean | string | void>
  • useModal() → returns lifecycle-scoped instance (auto-closes on unmount)