JavaScript popup library · v1.0

Alerts, forms and toasts that open from where you clicked.

Popet replaces the browser's alert boxes with dialogs your users can read, answer and fill in. One file, no dependencies, and it fits React, Vue, Angular and Laravel.

20 kBgzipped, styles included
0dependencies
22input types
4framework adapters
01

Try it first

Each tile opens a real popup. Notice where it grows from, and where it goes back to.

02

Examples

Pick one, read the code, run it. What you see is exactly what runs.


          
The result appears here.
03

Inputs

Every field is drawn by Popet, so it looks the same in every browser: a calendar and a clock, searchable and multiple selects, a drag-and-drop file zone, sliders, checkboxes, radios and switches.

Icons

04

Toasts

Small notices that stack, pause while you hover, and leave on their own. Four styles, five tones, nine positions.

Styles

Tones

Tones use the style you clicked last.

Positions

In code

Popet.toast({
  variant: 'solid',        // 'soft' (default), 'solid', 'outline' or 'minimal'
  icon: 'success',
  title: 'Success',
  text: 'JOHN DOE disconnected successfully',
  showCloseButton: true,
  position: 'top-end'
})

// Make one style the default for every toast
Popet.setDefaults({ variant: 'solid' })
05

Install and use

Import it, then call it once the page has loaded.

1. Import the plugin

npm install popetjs
import Popet from 'popetjs'

// or via CommonJS
const Popet = require('popetjs')

The default import brings its own styles. To manage the stylesheet yourself, import them separately:

import Popet from 'popetjs/core'
import 'popetjs/css'

2. Call it

Popet.fire({
  title: 'Error!',
  text: 'Do you want to continue?',
  icon: 'error',
  confirmButtonText: 'Cool'
})

Or the old-school way, from a CDN

<script src="https://cdn.jsdelivr.net/npm/popetjs@1/dist/popet.all.min.js"></script>

With the stylesheet separately, if you prefer:

<script src="https://cdn.jsdelivr.net/npm/popetjs@1/dist/popet.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/popetjs@1/dist/popet.min.css">

The font

Popet is set in Poppins. Load it on your page and the popups pick it up; without it they fall back to the system font. To use your own, set --popet-font.

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap">
06

Integrations with major frameworks

Popet is plain JavaScript, so the import above already works everywhere. These adapters add what each framework expects.

Import from popetjs/react and the content options accept JSX.

import Popet, { usePopet } from 'popetjs/react'

function DeleteButton({ user }) {
  const popet = usePopet()

  async function remove() {
    const ok = await popet.confirm({
      icon: 'warning',
      title: <>Delete <strong>{user.name}</strong>?</>,
      confirmButtonText: 'Delete'
    })
    if (ok) popet.toast('User deleted', 'success')
  }

  return <button onClick={remove}>Delete</button>
}
07

Forms

Pass fields and the popup becomes a form. Popet.form() adds a title bar and a button bar, and resolves the values, or null when cancelled.

Columns and rows

Popet.form({
  title: 'New customer',
  columns: 2,                 // fields per line
  fields: [
    { name: 'first', label: 'First name', required: true },
    { name: 'last', label: 'Last name' },
    { name: 'notes', label: 'Notes', type: 'textarea', col: 12 },
    { type: 'divider' },
    // an array is a row: equal widths
    [
      { name: 'city', label: 'City' },
      { name: 'zip', label: 'Postcode' },
      { name: 'joined', label: 'Joined', type: 'date' }
    ]
  ]
})

The grid has 12 tracks. columns sets the default width and col overrides it for one field: col: 6 is half, col: 12 is the full width. On phones every field takes the full width.

Validation

{
  name: 'username',
  label: 'Username',
  required: true,
  validate: async (value, values) => {
    if (value.length < 6) return 'Use at least 6 characters'
    if (await isTaken(value)) return 'Already taken'
    return { success: 'Username is available' }
  }
}

A field turns red with its message when it is wrong and green when it is right. Fields are checked as you leave them and again on submit. To show errors from your server, throw them from preConfirm in Laravel's shape:

throw { errors: { phone: ['Already registered'] } }

Field properties

PropertyWhat it does

Form controller

Passed to onFieldChange(name, value, form) and returned by Popet.getForm() while a form is open.

MethodWhat it does
08

Options

Everything you can pass to Popet.fire(). All of it is optional.

OptionDefaultWhat it does

Handling the result

Popet.fire() returns a promise that resolves when the popup closes. It never rejects.

const result = await Popet.fire({
  title: 'Save your changes?',
  showDenyButton: true,
  showCancelButton: true
})

if (result.isConfirmed) save()
else if (result.isDenied) discard()
else console.log(result.dismiss) // 'cancel', 'esc', ...
KeyMeaning
isConfirmedThe confirm button was used.
isDeniedThe deny button was used.
isDismissedIt was closed any other way.
valueThe input value, the form values, or whatever preConfirm returned.
dismissWhy it was dismissed: cancel, backdrop, close, esc, timer or replaced.
09

Methods

Shortcuts for the common cases, and controls for the popup that is open right now.

MethodWhat it does
10

Themes

Popet is styled with CSS variables. Choose a look below; the popups and this page change together, and the CSS you need is printed underneath.


      

Dark mode

Pick 'light', 'dark', or 'auto' to follow the visitor's system setting.

Popet.setDefaults({ theme: 'auto' })