Open source · MIT licensed · Zero runtime dependencies

Dialogs that behave themselves.

Forge Dialog is a dependency-free TypeScript dialog library built on the native <dialog> element. Accessible by default, themeable down to individual components, and small enough that an alert-only bundle stays under 6 KB gzipped.

Read the docs
npm install forgedialog

What you get

Everything below is in the published package — no plugins to install, no peer dependencies to satisfy.

Accessible by default

Focus is trapped and restored, Escape and backdrop clicks are wired up, and ARIA roles are set for you. Focus detection skips targets that only look focusable.

Styled per component

Colour and opacity for the surface, title, body, and border. Corner radius per corner. Shadows composed from an angle, distance, blur, and strength.

Hover, drag, and gestures

Opt-in hover restyling with lift and scale. Draggable dialogs with axis locks, bounds, keyboard movement, and persisted positions. Swipe-to-dismiss sheets.

More than alerts

Drawers, bottom sheets, lightboxes, loading states, toasts, a command palette, and persisted branching wizards — all from the same core.

Honest bundles

Tree-shakable entry points with enforced gzip budgets. Import forgedialog/alert and you pay for an alert, not the whole library.

Fits your stack

ESM, CJS, and a browser global, with adapters for React, Vue, Svelte, and Web Components. Typed throughout, SSR-safe, and clean under a strict CSP.

Playground

Change anything below and the dialog restyles live. The code updates with it — copy it and you have the same dialog in your app.

Examples

The everyday calls. Each returns a promise, so there is no callback nesting.

Replace the browser built-ins

import { alert, confirm, prompt } from 'forgedialog';
import 'forgedialog/style.css';

await alert('Saved successfully.', { title: 'Success' });

const ok = await confirm('Delete this item?');

const name = await prompt('What is your name?', {
  validate: (value) => value.trim() ? true : 'Name is required',
});

Build your own dialog

import { open } from 'forgedialog';

const dialog = open({
  title: 'Custom dialog',
  content: (container) => container.append(renderForm()),
  buttons: [
    { text: 'Cancel', onClick: (i) => i.close(null) },
    { text: 'Save', role: 'primary', onClick: (i) => i.close(read()) },
  ],
});

const result = await dialog.whenClosed();

Pay only for what you use

// Just an alert, and nothing else.
import { alert } from 'forgedialog/alert';
import 'forgedialog/style/core.css';

// Opt into the richer appearance options on a focused entry.
import 'forgedialog/appearance';

Beyond dialogs

import { drawer, toast, wizard } from 'forgedialog';

// A duration of 0 keeps the toast until dismiss() is called.
const upload = toast('Uploading…', { duration: 0 });

drawer({ title: 'Settings', side: 'right', content: renderSettings });

const data = await wizard({ steps }).result;

At a glance

The short, factual version — the numbers come from the size budgets enforced on every pull request.

Name Forge Dialog
npm package forgedialog
License MIT
Runtime dependencies None
Size ≈15 KB gzipped for the full bundle; ≈5.5 KB for an alert-only build
Written in TypeScript, with types for both ESM and CommonJS
Browsers Chrome/Edge 88+, Firefox 78+, Safari 15.4+
Node.js 20 or newer, for package tooling and SSR imports
Frameworks Works standalone; adapters for React, Vue, Svelte, and Web Components
Source github.com/cmm-cmm/ForgeDialog

Frequently asked questions

What is Forge Dialog?

Forge Dialog is an open-source TypeScript library for building dialogs on the web: alerts, confirmations, prompts, drawers, bottom sheets, lightboxes, loading states, toasts, command palettes, and multi-step wizards. It is published on npm as forgedialog, licensed MIT, and has no runtime dependencies.

How large is Forge Dialog?

The complete bundle is roughly 15 KB gzipped. An application that imports only forgedialog/alert ships roughly 5.5 KB gzipped, because every entry point is tree-shakable and carries a size budget that continuous integration enforces on each pull request.

Does Forge Dialog need a framework?

No. Forge Dialog is framework-agnostic and works with plain JavaScript or a <script> tag. Optional adapters for React, Vue, Svelte, and Web Components ship in the same package.

Is Forge Dialog accessible?

Yes. It renders the native <dialog> element, traps focus while a dialog is open and restores it to the trigger on close, wires up Escape and backdrop dismissal, and sets role="dialog" or role="alertdialog" automatically. Motion honours the prefers-reduced-motion setting.

How is it different from window.alert, confirm, and prompt?

The browser built-ins block the main thread, cannot be styled, and offer three fixed shapes. Forge Dialog returns promises instead of blocking, can be styled down to individual components, and covers drawers, sheets, forms, and wizards as well.

Can the appearance be customised?

Yes. Colour and opacity are set independently for the surface, title, body, and border; corner radius can be set per corner; shadows are composed from an angle, distance, blur, spread, and strength; hover effects are opt-in per dialog. All of it can be changed while a dialog is open by calling update().

Which browsers and runtimes are supported?

Chrome and Edge 88+, Firefox 78+, and Safari 15.4+, plus Node.js 20 or newer for package tooling and server-side imports. The bundle contains no eval and no new Function, so it runs under a strict Content Security Policy.

Is Forge Dialog free to use commercially?

Yes. Forge Dialog is released under the MIT license, which permits commercial use, modification, and redistribution.

How does Forge Dialog compare to SweetAlert2 and Bootstrap modals?

Forge Dialog builds on the browser's native <dialog> element, so focus trapping, the top layer, and inert backgrounds come from the platform rather than from JavaScript. It has no runtime dependencies, where a Bootstrap modal needs Bootstrap's CSS and JavaScript. Every entry point is tree-shakable, so an application that only needs an alert ships about 5.5 KB gzipped rather than the whole library. It is written in TypeScript and ships types for both ESM and CommonJS.