npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

electron-dynamic-island

v1.1.0

Published

A robust, drop-in Dynamic Island notification system for macOS Electron apps.

Readme

Table of Contents

Features

  • Hardware Notch Integration: Automatically detects and aligns with the MacBook notch.
  • Robust Sizing:
    • Dynamic Mode: Expands fluidly to fit content (icon + text) while maintaining perfect optical alignment.
    • Fixed Mode: Standard notification size for consistent UI.
  • Native Animations: Smooth, spring-based animations for entry, exit, and state changes.
  • Sound System: Integrated sound effects for success, error, and connection states.
  • TypeScript Ready: Includes built-in type definitions (index.d.ts). This provides full IntelliSense and autocomplete support in VS Code, even if you're writing plain JavaScript.

Installation

npm install electron-dynamic-island

Usage

1. Initialize in Main Process

Initialize the island in your main.js or index.js after the app is ready.

const { app } = require('electron');
const { DynamicIsland } = require('electron-dynamic-island');

let island;

app.whenReady().then(() => {
    island = new DynamicIsland({
        debug: false, // Enable for logging
        enableSounds: true,
        sizing: { type: 'fixed' } // Default mode
    });

    island.init();
});

2. Trigger Notifications

You can trigger notifications from anywhere in your main process.

Basic Success Notification

island.show({
    type: 'success',
    message: 'AirPods Connected',
    icon: 'bluetooth' // Built-in icon
});

Dynamic Mode (Auto-expanding)

Use dynamic mode for content that varies in length or icon size. The window will automatically resize and center itself.

island.show({
    type: 'info',
    message: 'Downloading Update...',
    iconSize: 40, // Larger icon
    sizing: { 
        type: 'dynamic', 
        height: true, // Allow height expansion
        width: true   // Allow width expansion
    }
});

Custom Icon & Sound

island.show({
    type: 'success',
    message: 'Device Paired',
    icon: '/path/to/icon.png', // Local file path
    soundFile: '/path/to/sound.mp3',
    duration: 3000
});

3. Best Practice: Check Support

Since the Dynamic Island only works on MacBooks with a notch and on the internal display, you should check for support before triggering it. If not supported, fallback to a standard Electron notification.

if (island.isSupported()) {
    island.show({
        type: 'success',
        message: 'Connected'
    });
} else {
    // Fallback for older Macs, external displays, or Windows/Linux
    new Notification({ title: 'Connected', body: 'Device connected successfully' }).show();
}

4. Advanced Usage

Triggering on Events

You can hook the island into any Electron event or your own application logic.

// Example: Show notification when a file download completes
ipcMain.on('download-complete', (event, fileName) => {
    island.show({
        type: 'success',
        message: `${fileName} Downloaded`,
        icon: 'usb-c'
    });
});

// Example: Show warning on battery low
const { powerMonitor } = require('electron');
powerMonitor.on('on-battery', () => {
    island.show({
        type: 'warning',
        message: 'On Battery Power',
        icon: 'warning'
    });
});

Handling Multiple Notifications

If you call island.show() while a notification is already visible, the new notification will instantly replace the current one with a smooth transition. This is useful for updating status (e.g., "Connecting..." -> "Connected").

// 1. Show initial state
island.show({
    type: 'info',
    message: 'Connecting...',
    icon: 'spin', // Built-in spinner animation
    animation: 'spin'
});

// 2. Update state after operation finishes
setTimeout(() => {
    island.show({
        type: 'success',
        message: 'Connected'
    });
}, 2000);

5. Animations

The library includes several native-feeling animations for the icon. Pass the name to the animation option in show().

| Animation | Description | | :--- | :--- | | pulse | A soft, breathing pulse effect. | | bounce | A playful vertical bounce. | | spin | A smooth 360-degree rotation. | | wobble | A quirky back-and-forth shake. | | fade | A simple, elegant fade-in. | | slide | Slides down from the top. | | none | Disables all icon animations. |

6. Customization

You can fine-tune the look to match your app's design. If you don't provide these values, the library uses native macOS defaults.

| Option | Default Value | Description | | :--- | :--- | :--- | | fontFamily | -apple-system (SF Pro) | Matches the system UI font. | | fontSize | 14px | Standard macOS notification text size. | | color | #FFFFFF (White) | High-contrast white text. | | iconGap | 18px | Standard spacing between icon and text. |

island.show({
    message: 'System Alert',
    type: 'error',
    // Overrides:
    fontFamily: 'Helvetica Neue, sans-serif',
    fontSize: '12px',
    color: '#FF0000',
    iconGap: 24
});

7. Built-in Icons

The library comes with a set of polished, native-style SVG icons. You can use them by passing their name to the icon property.

Built-in Icons Preview

| Icon Name | Description | | :--- | :--- | | check | A bold checkmark (default for success). | | usb-c | A minimalist USB-C port symbol. | | x | A crisp X symbol (default for error). | | warning | A standard warning triangle. | | info | A circle with an 'i'. | | bluetooth | A minimalist Bluetooth symbol. |

// Use a built-in icon explicitly
island.show({
    message: 'Bluetooth On',
    icon: 'bluetooth'
});

8. Notification Types

The type option determines the default color, icon, and sound for the notification.

| Type | Color | Default Icon | Default Sound | Glow Effect | Default Animation | | :--- | :--- | :--- | :--- | :--- | :--- | | success | Green (#4CD964) | Checkmark | Success Chime | Yes (Green) | pop-in | | error | Red (#FF3B30) | X Symbol | Error Sound | Yes (Red) | shake | | warning | Yellow (#FFD60A) | Warning Triangle | None | Yes (Yellow) | pop-in | | info | Blue (#0A84FF) | Info Circle | None | Yes (Blue) | pop-in |

Note: You can override any of these defaults by passing specific color, icon, or soundFile options. Tip: You can disable the glow effect by setting glow: false. You can also override the animation by passing animation: 'none' or another type.

API Reference

new DynamicIsland(options)

| Option | Type | Default | Description | | :--- | :--- | :--- | :--- | | enableSounds | boolean | true | Play built-in sounds. | | sizing | object | { type: 'fixed' } | Default sizing configuration. | | debug | boolean | false | Log internal debug messages to the console. | | devMode | boolean | false | Show a red dashed boundary around the window for visual debugging. |

island.isSupported()

Returns true if the app is running on a Mac with a notch and the internal display is active. Use this to determine whether to show the Dynamic Island or a fallback notification.

island.show(options)

| Option | Type | Description | | :--- | :--- | :--- | | type | 'success' \| 'error' \| 'info' \| 'warning' | Determines color and default icon. | | message | string | The text to display. | | icon | string | Path to image, SVG string, or preset ('bluetooth'). | | iconSize | number | Size of the icon (width & height) in pixels. | | iconWidth | number | Width of the icon in pixels (overrides iconSize). | | iconHeight | number | Height of the icon in pixels (overrides iconSize). | | sizing | object | Override sizing configuration (see below). | | duration | number | Time in ms before auto-hiding (default 4500). | | animation | string | Animation type (see Animations section). | | fontFamily | string | Custom font family for the message. | | fontSize | string | Custom font size (e.g., '14px'). | | color | string | Custom text color (hex, rgb, or name). | | glow | boolean | Enable/disable the icon glow effect (default true). | | iconGap | number | Space between icon and text in pixels. | | soundFile | string | Path to custom sound file. | | devMode | boolean | Override global devMode setting. |

Icon Sizing

  • Square Icons: Use iconSize (e.g., 40) to set both width and height.
  • Non-Square Icons: Use iconWidth and iconHeight for custom dimensions (e.g., landscape images).

Example: Landscape Icon (AirPods)

island.show({
    message: 'AirPods Pro',
    icon: '/path/to/airpods.png',
    iconWidth: 100,
    iconHeight: 50, // Prevents extra vertical space
    sizing: { type: 'dynamic', height: true, width: true }
});

Sizing Modes

1. Fixed Mode (Default)

The notification stays at a consistent, standard size (height: 100px).

  • Best for: Simple alerts, consistent UI feel.
  • Behavior:
    • Text: Long text is truncated with an ellipsis (...).
    • Icons: Constrained to the container. Large icons will be clipped if they exceed the fixed height.

2. Dynamic Mode

The notification fluidly expands to fit your content.

  • Best for: Variable content, large images, multi-line text.
  • Behavior:
    • Height: Calculates exact height needed based on iconHeight + padding + notch clearance.
    • Width: Expands horizontally if sizing.width: true.
    • Alignment: Optically centers content below the hardware notch using asymmetric padding.

Sizing Configuration

The sizing object controls how the notification behaves.

{
    type: 'dynamic', // 'fixed' or 'dynamic'
    height: true,    // Allow height to expand (Dynamic Mode only)
    width: true,     // Allow width to expand (Dynamic Mode only)
    notchHeight: 40, // Override detected notch height (optional)
    verticalOffset: 0 // Fine-tune vertical position (pixels)
}

How It Works

  1. Detection: The library checks if the app is running on a compatible MacBook (M1 Pro/Max, M2/M3 Air/Pro) using system model identifiers.
  2. Positioning: It calculates the screen center and aligns the notification window directly under the hardware notch.
  3. Optical Alignment: In Dynamic Mode, the library uses asymmetric padding (more top padding) to optically center the content below the physical notch, ensuring it looks balanced to the human eye.
  4. Window Management: The Electron window is transparent and click-through, resizing dynamically based on the content to prevent clipping while maintaining performance.

Technical Note

It is important to note that macOS does not have a public API for "Dynamic Island" interactions. Native apps like Alcove and libraries like DynamicNotchKit leverage private system APIs or low level Swift windowing controls to achieve this effect, which are not directly accessible to Electron.

This library is "smart" because it reverse engineers that behavior for the web stack. It calculates the precise screen coordinates of the notch (which Electron does not natively detect) and manages a transparent, click through window that sits on top of everything.

Visually, it creates the illusion of the notch "expanding" by drawing a seamless black overlay that extends downwards and outwards. It uses hardware accelerated CSS transitions to mimic the exact spring physics of macOS, allowing an Electron app to feel just as native as a Swift application without the need for native code.

Origin

The core logic for this library was originally developed for Serial Bridge to handle device connection and disconnection status updates. When I couldn't find any existing solution to achieve this effect in Electron, I built it from scratch. After extensive testing and refinement within that application, I decided to extract, polish, and expand the functionality into this standalone library to help other Electron developers build buttery smooth, native experiences.

Running Examples

The repository includes example scripts to demonstrate the library's capabilities.

  1. Basic Example: Shows a custom icon (AirPods) with dynamic sizing.

    npm start
  2. Advanced Example: Demonstrates sequential updates (Loading -> Success) and event triggering.

    npm run example:advanced

License

MIT