Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

svelte-simple-modal

flekschas34.2kMIT2.0.0TypeScript support: included

A small and simple modal for Svelte

svelte, modal, popup, dialog

readme

Svelte Simple Modal

A simple, small, and content-agnostic modal for Svelte.


NPM Version Build Status File Size Code Style Prettier Demo

simple-modal

Live demo: https://svelte.dev/playground/b95ce66b0ef34064a34afc5c0249f313?version=5.17.3

Works with: Svelte >=v5!

[!NOTE]
If you want to use Svelte Simple Modal with Svelte v3 or v4, check out version 1.

Table of Contents

Install

npm install --save svelte-simple-modal

Rollup Setup

Make sure that the main application's version of svelte is used for bundling by setting rollup-plugin-node-resolve's dedupe option as follows:

import resolve from 'rollup-plugin-node-resolve';

export default {
  plugins: [
    resolve({
      dedupe: ['svelte', 'svelte/transition', 'svelte/internal'], // important!
    }),
  ],
};

Usage

Import the Modal component into your main app component (e.g., App.svelte).

The modal is exposing two context functions:

  • open() opens a component as a modal.
  • close() simply closes the modal.
<!-- App.svelte -->
<script>
  import Content from './Content.svelte';
  import Modal from 'svelte-simple-modal';
</script>

<Modal><Content /></Modal>


<!-- Content.svelte -->
<script>
  import { getContext } from 'svelte';
  import Popup from './Popup.svelte';
  const { open } = getContext('simple-modal');
  const showSurprise = () => open(Popup, { message: "It's a modal!" });
</script>

<p><button on:click={showSurprise}>Show me a surprise!</button></p>


<!-- Popup.svelte -->
<script>
  export let message = 'Hi';
</script>

<p>🎉 {message} 🍾</p>

Demo: https://svelte.dev/repl/52e0ade6d42546d8892faf8528c70d30

Usage with TypeScript

You can use the Context type exported by the package to validate the getContext function:

import type { Context } from 'svelte-simple-modal';
const { open } = getContext<Context>('simple-modal');
// ...

Usage With a Svelte Store

Alternatively, you can use a Svelte store to show/hide a component as a modal.

<!-- App.svelte -->
<script>
  import { writable } from 'svelte/store';
  import Modal, { bind } from 'svelte-simple-modal';
  import Popup from './Popup.svelte';
  const modal = writable(null);
  const showModal = () => modal.set(bind(Popup, { message: "It's a modal!" }));
</script>

<Modal show={$modal}>
  <button on:click={showModal}>Show modal</button>
</Modal>

Demo: https://svelte.dev/repl/aec0c7d9f5084e7daa64f6d0c8ef0209

The <Popup /> component is the same as in the example above.

To hide the modal programmatically, simply unset the store. E.g., modal.set(null).

Styling

The modal comes pre-styled for convenience but you can easily extent or replace the styling using either custom CSS classes or explicit CSS styles.

Custom CSS classes can be applied via the classBg, classWindow, classWindowWrap, classContent, and classCloseButton properties. For instance, you could customize the styling with TailwindCSS as follows:

<Modal
  show={$modal}
  unstyled={true}
  classBg="fixed top-0 left-0 w-screen h-screen flex flex-col justify-center bg-orange-100/[.9]"
  classWindowWrap="relative m-2 max-h-full"
  classWindow="relative w-40 max-w-full max-h-full my-2 mx-auto text-orange-200 rounded shadow-md bg-indigo-900"
  classContent="relative p-2 overflow-auto"
  closeButton={false}
>
  <button on:click={showModal}>Show modal</button>
</Modal>

Demo: https://svelte.dev/repl/f2a988ddbd5644f18d7cd4a4a8277993

Note: to take full control over the modal styles with CSS classes you have to reset existing styles via unstyled={true} as internal CSS classes are always applied last due to Svelte's class scoping.

Alternatively, you can also apply CSS styles directly via the styleBg, styleWindow, styleWindowWrap, styleContent, and styleCloseButton properties. For instance:

<Modal
  show={$modal}
  styleBg={{ backgroundColor: 'rgba(255, 255, 255, 0.85)' }}
  styleWindow={{ boxShadow: '0 2px 5px 0 rgba(0, 0, 0, 0.15)' }}
>
  <button on:click={showModal}>Show modal</button>
</Modal>

Demo: https://svelte.dev/repl/50df1c694b3243c1bd524b27f86eec51

Server-Side Rendering

With SvelteKit you can enable SSR using the browser environmental variable as follows:

<script>
  import Modal from 'svelte-simple-modal';
  import { browser } from '$app/env';
</script>

{#if browser}
  <Modal>
    <Content />
  </Modal>
{/if}

Alternatively, you can enable SSR by dynamically importing svelte-simple-modal in onMount() as follows:

import { onMount } from 'svelte';

onMount(async () => {
  const svelteSimpleModal = await import('svelte-simple-modal');
  Modal = svelteSimpleModal.default;
});

Accessibility

The library applies the following WAI-ARIA guidelines for modal dialogs automtically:

  • aria-modal="true" and role="dialog" are applied automatically
  • The tab focus is trapped in the modal
  • The modal is closed on pressing the esc key

To further improve the accessibility you'll have to either provide a label via ariaLabel or reference a title element via ariaLabelledBy. The ariaLabel is automatically omitted when ariaLabelledBy is specified.

API

Component API

The <Modal /> component accepts the following optional properties:

Property Type Default Description
show Component | null null A Svelte component to show as the modal. See Store API for details.
id string | null null Element ID to be assigned to the modal's root DOM element.
ariaLabel string | null null Accessibility label of the modal. See W3C WAI-ARIA for details.
ariaLabelledBy string | null null Element ID holding the accessibility label of the modal. See W3C WAI-ARIA for details.
closeButton Component | boolean true If true a button for closing the modal is rendered. You can also pass in a custom Svelte component to have full control over the styling.
closeOnEsc boolean true If true the modal will close when pressing the escape key.
closeOnOuterClick boolean true If true the modal will close when clicking outside the modal window.
transitionBg function svelte.fade Transition function for the background.
transitionBgProps BlurParams | FadeParams | FlyParams | SlideParams {} Properties of the transition function for the background.
transitionWindow function svelte.fade Transition function for the window.
transitionWindowProps BlurParams | FadeParams | FlyParams | SlideParams {} Properties of the transition function for the window.
classBg string | null null Class name for the background element.
classWindowWrap string | null null Class name for the modal window wrapper element.
classWindow string | null null Class name for the modal window element.
classContent string | null null Class name for the modal content element.
classCloseButton string | null null Class name for the built-in close button.
styleBg Record<string, string | number> {} Style properties of the background.
styleWindowWrap Record<string, string | number> {} Style properties of the modal window wrapper element.
styleWindow Record<string, string | number> {} Style properties of the modal window.
styleContent Record<string, string | number> {} Style properties of the modal content.
styleCloseButton Record<string, string | number> {} Style properties of the built-in close button.
unstyled boolean false When true, the default styles are not applied to the modal elements.
disableFocusTrap boolean false If true elements outside the modal can be in focus. This can be useful in certain edge cases.
isTabbable (node: Element): boolean internal function A function to determine if an HTML element is tabbable.
key string "simple-modal" The context key that is used to expose open() and close(). Adjust to avoid clashes with other contexts.
setContext function svelte.setContent You can normally ingore this property when you have configured Rollup properly. If you want to bundle simple-modal with its own version of Svelte you have to pass setContext() from your main app to simple-modal using this parameter.

Component Events

The <Modal /> component dispatches the following events:

  • open: dispatched when the modal window starts to open.
  • opened: dispatched when the modal window opened.
  • close: dispatched when the modal window starts to close.
  • closed: dispatched when the modal window closed.

Alternatively, you can listen to those events via callbacks passed to open() and close().

Context API

Svelte Simple Modal uses Svelte's context API to expose the open() and close() methods. You can get these methods as follows:

const { open, close } = getContext('simple-modal');

# open(Component, props = {}, options = {}, callbacks = {})

Opens the modal with <Component {props}> rendered as the content. options can be used to adjust the modal behavior once for the modal that is about to be opened. The options allows to customize all parameters except key and setContext:

{
  closeButton: false,
  closeOnEsc: false,
  closeOnOuterClick: false,
  transitionBg: fade,
  transitionBgProps: {
    duration: 5000
  },
  transitionWindow: fly,
  transitionWindowProps: {
    y: 100,
    duration: 250
  },
  styleBg: { backgroundImage: 'http://example.com/my-background.jpg' },
  styleWindow: { fontSize: '20em' },
  styleContent: { color: 'yellow' },
  styleCloseButton: { width: '3rem', height: '3rem' },
  disableFocusTrap: true
}

# close(callbacks = {})

Closes the modal. Similar to open(), this method supports adding callbacks for the closing transition:

{
  onClose: () => { /* modal window starts to close */ },
  onClosed: () => { /* modal window closed */ },
}

Callbacks are triggered at the beginning and end of the opening and closing transition. The following callbacks are supported:

{
  onOpen: () => { /* modal window starts to open */ },
  onOpened: () => { /* modal window opened */ },
  onClose: () => { /* modal window starts to close */ },
  onClosed: () => { /* modal window closed */ },
}

Store API

You can also use Svelte stores to open a modal using the <Modal />'s show property as follows:

<!-- App.svelte -->
<script>
  import { writable } from 'svelte/store';
  import Modal from 'svelte-simple-modal';
  import Popup from './Popup.svelte';
  const modal = writable(null);
  const showModal = () => modal.set(Popup);
</script>

<Modal show={$modal}>
  <button on:click={showModal}>Show modal</button>
</Modal>

<!-- Popup.svelte -->
<p>🎉 Hi 🍾</p>

Demo: https://svelte.dev/repl/6f55b643195646408e780ceeb779ac2a

An added benefit of using stores is that the component opening the modal does not have to be a parent of <Modal />. For instance, in the example above, App.svelte is toggling Popup.svelte as a modal even though App.svelte is not a child of <Modal />.

Bind Props to a Component Shown as a Modal

Sometimes you want to pass properties to the component shown as a modal. To accomplish this, you can use our bind() helper function as follows:

<script>
  import { bind } from 'svelte-simple-modal';
  import Popup from './Popup.svelte';
  import { modal } from './App.svelte';

  modal.set(bind(Popup, { name: 'custom name' }));
</script>

If you've worked with React/JSX then think of const c = bind(Component, props) as the equivalent of const c = <Component ...props />.

FAQ

Custom Close Button

This feature requires Svelte >=v3.19!

Unfortunately, it's not possible to adjust all styles of the built-in close button via the styleCloseButton option. If you need full control you can implement your own Svelte component and use that as the close button. To do so pass your component via the closeButton option to <Modal /> or open().

For example, your close button might look as follows:

<!-- CloseButton.svelte -->
<script>
  // This property is used by Modal.svelte to pass down the close function
  export let onClose;
</script>

<button on:click={onClose}>Custom Close Button</button>

<style>
  /* Customize to your liking */
  button {
    position: absolute;
    top: -3rem;
    right: 0;
  }
</style>

Now you can set it as the default close button by passing it to <Modal /> as follows:

<!-- App.svelte -->
<script>
  import Content from './Content.svelte';
  import CloseButton from './CloseButton.svelte';
  import Modal from 'svelte-simple-modal';
</script>

<Modal closeButton={CloseButton}>
  <Content />
</Modal>

Or you can pass CloseButton to open() as shown below:

<!-- Content.svelte -->
<script>
  import { getContext } from 'svelte';
  import Surprise from './Surprise.svelte';
  import CloseButton from './CloseButton.svelte';

  const { open } = getContext('simple-modal');

  const showSurprise = () => {
    open(Surprise, { message: "It's a modal!" }, { closeButton: CloseButton });
  };
</script>

<p><button on:click={showSurprise}>Show me a surprise!</button></p>

License

MIT

changelog

2.0.0

BREAKING CHANGE: Upgraded to Svelte v5. This change is unfortunately not backwards compatible with Svelte 3 or 4. If you want to use Svelte Simple Modal with Svelte v3 or v4, check out version 1 (https://github.com/flekschas/svelte-simple-modal/tree/v1) ([#119]https://github.com/flekschas/svelte-simple-modal/pull/119)

1.6.2

  • Fix Svelte type resolve issue (#107)

1.6.1

  • Fix Svelte resolve warning (#103)

1.6.0

  • Enable usage with Svelte v4 (the library remains compatible with v3)
  • Update third-party libs

1.5.2

  • Fix and expose context types. This is useful as an interim solution for (#88). E.g.:

    import { getContext } from 'svelte';
    import type { Context } from 'svelte-simple-modal';
    const { open, close } = getContext('simple-modal') as Context;

1.5.1

1.5.0

1.4.6

1.4.5

  • Fix min-height issue with Safari iOS 15 (#64)

1.4.4

  • One more type definition improvement

1.4.3

  • Further improve type definitions

1.4.2

  • Improve type definitions

1.4.1

  • Avoid submitting a <form /> if it wraps <Modal /> by setting the close button type to button. (#84) Shoutout to @jnysteen!

1.4.0

  • Skip untabbable element (#82) Massive thanks to @jassenjj 🙏
  • Added isTabbable property to allow users to override the tab-check. E.g., one might want to use a more the tabbable library.

1.3.4

  • Respect the tab order defined by tabindex attributes of elements within a modal (#80)

1.3.3

  • Fix accidentally removed global export of bind()

1.3.2

1.3.1

1.3.0

  • Add classBg, classWindow, classWindowWrap, classContent, and classCloseButton properties to applying custom CSS classes to the modal elements (#62)
  • Add unstyled to prevent applying default styles and providing full control over the modal styling

1.2.0

  • Add ariaLabel and ariaLabelledBy to props/options to support improved accessibility (#37)
  • Add aria-label="Close modal" to the default close button component (#37)

1.1.3

  • Add "type": "module" in package.json (#57)

1.1.2

  • Properly export bind() (#58)

1.1.1

  • Add type annotations via JSDocs, Typescript, and Sveld (#52)

1.1.0

  • Add disableFocusTrap property for disabling the focus trap behavior (#49)

1.0.4

  • Make sure that the modal only closes when it's open to fix (#53)

1.0.3

  • Remove accidentally added console.log (#50) and forbid console.log via linting

1.0.2

1.0.1

  • Remove position: fixed from body on opening a modal as it appears that the fixed positioning is not needed to avoid scrolling (#38)

1.0.0

Woohoo 🎉 Thanks everyone who uses and supports this library. This release really just introduces a bug fix but given that most (hopefully all 🤞) of the features are stable, I decided to finally release v1.

  • Fixes a scrolling issue introduced with #45 while still keeping the library compatible with SvelteKit (#47)

0.10.4

  • Default bg width/height to innerWidth/innerHeight (#40)
  • Make compatible with svelte kit (#45)

0.10.3

  • Avoid exception when converting falsy value to CSS (#42)

0.10.2

  • Improve outer click handling and avoid accidental closing of the modal (#39)

0.10.1

  • Harmonize on:open/on:opening and on:close/on:closing event (#33 and #34). Note, on:opening and on:closing are still being dispatched for backward compatibility but they will be remove in future versions so please switch over to on:open and on:close.

0.10.0

  • Disable body scrolling while modal is open (#31 -> #28)
  • Fixed a visual glitch where previously customized styles were not reset

0.9.0

  • Added <Modal show={PopupComponent} /> as an alternative to other the context API (#22)

0.8.1

  • Replace too strict isSvelteComponent with isFunction (#27)

0.8.0

  • Dispatch open, opened, close, and closed events (#25)

0.7.0

  • Allow customizing the window wrapper via styleWindowWrap (#24)

0.6.1

  • Fall back gracefully for older versions of Svelte (<3.19) when SvelteComponent for the custom close button is not available (#21)

0.6.0

  • Allow customizing the close button (#20)

0.5.0

  • Trap focus in modal (#17)
  • Add aria information

0.4.2

  • Explicitly expose the event object (#13)

0.4.1

  • Remove wrapping div element as it's not necessary (#12)

0.4.0

0.3.1

  • Fix non-reactive component (#11)

0.3.0

0.2.2

  • Update package-lock.json file

0.2.1

0.2.0

  • Allow customizing the modal style on open(Component, props, styling). This can be used for content-aware theming.

0.1.1

  • Robustify overflow

0.1.0

  • First release