Agent Skills

modal

frontendlobehub4.1K installs

Use for modals, dialogs and confirmations with createModal, confirmModal, ModalHost or base-ui modal APIs.

Install

npx skills add https://github.com/lobehub/lobehub --skill modal
SKILL.md

Modal Imperative API Guide

Recommended: @lobehub/ui/base-ui

New code should use the base-ui modal stack (headless primitives, not antd Modal):

  • createModal, confirmModal, ModalHost from @lobehub/ui/base-ui
  • useModalContext from @lobehub/ui/base-ui inside modal content

Body slot: pass content (or children; runtime uses content ?? children).

Global ModalHost (required)

Base-ui createModal renders through a separate host from the root package. The app must mount ModalHost from @lobehub/ui/base-ui once near the root (e.g. next to other global hosts). Without it, createModal calls will not appear.

If the project only mounts ModalHost from @lobehub/ui, add a second lazy ModalHost from @lobehub/ui/base-ui until all imperative modals are migrated.

Why imperative?

Mode Characteristics Recommended
Declarative open state + <Modal /> ❌
Imperative Call createModal(), no local state ✅

File structure

features/
└── MyFeatureModal/
    ├── index.tsx            # export createXxxModal
    └── MyFeatureContent.tsx # modal body

1. Content (MyFeatureContent.tsx)

'use client';

import { useModalContext } from '@lobehub/ui/base-ui';
import { useTranslation } from 'react-i18next';

export const MyFeatureContent = () => {
  const { t } = useTranslation('namespace');
  const { close } = useModalContext();

  return <div>{/* ... */}</div>;
};

2. createModal (index.tsx)

'use client';

import { createModal } from '@lobehub/ui/base-ui';
import { t } from 'i18next';

import { MyFeatureContent } from './MyFeatureContent';

export const createMyFeatureModal = () =>
  createModal({
    content: <MyFeatureContent />,
    footer: null,
    maskClosable: true,
    styles: {
      content: { overflow: 'hidden', padding: 0 },
    },
    title: t('myFeature.title', { ns: 'setting' }),
    width: 'min(80%, 800px)',
  });

3. Usage

import { createMyFeatureModal } from '@/features/MyFeatureModal';

const handleOpen = useCallback(() => {
  createMyFeatureModal();
}, []);

return <Button onClick={handleOpen}>Open</Button>;

i18n

  • Content: useTranslation in components.
  • createModal options: import { t } from 'i18next' where hooks are unavailable.

useModalContext

const { close, setCanDismissByClickOutside } = useModalContext();

Closing: which callback actually fires

close() — from useModalContext() inside the content, or from the returned ModalInstance — only flips the stack entry to open: false. It does not go through base-ui's dismissal path, so:

callback user dismissal (Esc / backdrop / header ✕) close() from content or instance
onOpenChange fires does not fire
onOpenChangeComplete fires with false fires with false

Put caller-side cleanup (clearing an editing flag, resetting the provider's open state) on onOpenChangeComplete. Wiring it to onOpenChange looks correct until a footer button closes the modal, and then the caller never learns it went away — typically leaving a flag set so the modal cannot be reopened.

createModal only ever completes with false (the imperative renderer supplies the argument itself and never forwards the prop to base-ui), but still guard on it — other base-ui primitives such as DropdownMenu do report both directions, and the guard keeps the call site from depending on that difference:

onOpenChangeComplete: (open) => {
  if (!open) onClosed?.();
},

Common options (base-ui)

ImperativeModalProps builds on BaseModalProps: title, width, maskClosable, open, onOpenChange, footer, styles / classNames (keys: backdrop, popup, header, title, close, content, …).

Property Notes
content Main body (preferred name vs children)
maskClosable Click outside to dismiss
styles.* Semantic regions, not antd styles.body

Confirm

import { confirmModal } from '@lobehub/ui/base-ui';

confirmModal({
  title: '…',
  content: '…',
  okText: '…',
  cancelText: '…',
  onOk: async () => {},
});

Legacy: @lobehub/ui (root)

Older call sites use createModal from @lobehub/ui, which is typed as antd Modal props (children, allowFullscreen, getContainer, destroyOnHidden, styles.body, etc.). Prefer migrating new work to @lobehub/ui/base-ui.

Examples (legacy): src/features/SkillStore/index.tsx, src/features/LibraryModal/CreateNew/index.tsx.


Examples

  • Base-ui (preferred): follow sections above; ensure base-ui ModalHost is mounted.
  • Legacy: src/features/SkillStore/index.tsx, src/features/LibraryModal/CreateNew/index.tsx

Related skills

vercel-react-best-practicesvercel-labs751KReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.vercel-composition-patternsvercel-labs361KReact composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.vercel-react-view-transitionsvercel-labs135KGuide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions viewpick-ui-libraryemilkowalski124KPick the right library for a given frontend task from a curated, opinionated list — numbers, OTP inputs, charts, command menus, virtualization, drag and drop, toasts, state, styling, and more. Only runs when explicitly invoked; it does not trigger on its own.

Search skills and MCP servers

Fuzzy search across 23,137 skills and servers