Simple modal manager for React that does the trick.
Slick Modal is a small modal state manager for React focusing on simplicity and ease of use. It is inspired by nice-modal-react.
You can show and hide modals in a declarative way, with typesafe async and client component("use client") support.
npm install slick-modal
yarn add slick-modal
pnpm install slick-modalStore, Provider and Outlet
"use client";import{defaultStore,createModalStore,createModalControl,SlickModalProvider,SlickModalOutlet,}from"slick-modal";// default modal store is created by default and used if no store context is specifedconsole.log(defaultStore);constmodalStore=createModalStore();constmodalControl=createModalControl(modalStore);// you can externally control the modal store via modalControlmodalControl.open(<p>Hi There</p>);modalControl.open(<p>Hi There</p>,{key: "my-modal-key",}).then((data)=>{alert(data);});modalControl.resolve("my-modal-key","done");modalControl.close("my-modal-key");modalControl.closeAll();// important: you don't have to specify default provider.// if no provider is specified, slick modal will use the default provider.functionWithProvider({ children }: {children: React.ReactNode}){return(// if no explict store is provided, the provider will automatically create a new internal store.<SlickModalProviderstore={modalStore}>{children}<ModalOutlet/></SlickModalProvider>);}// default outletfunctionModalOutlet(){return<SlickModalOutlet/>;}// you can also use render props to customize the outletfunctionCustomModalOutlet(){return(<SlickModalOutlet>{(modals)=>{return(<divclassName={modals.length>0
? "bg-zinc-100 h-full -mx-4 p-4 rounded"
: undefined}>{modals.map((modal,i)=>(<divclassName="absolute inset-x-0"style={{marginTop: `${i*4}rem`,}}key={modal.state.key}>{modal.element}</div>))}</div>);}}</SlickModalOutlet>);}Single use modals
"use client";import{useModal,createModal}from"slick-modal";functionModal(){const{ key, hide, resolve, stackIndex }=useModal();return(<div><p>
This is modal {key} at {stackIndex}</p><buttononClick={()=>{resolve(`modal ${key} resolved`);hide();}}>
close
</button></div>);}functionModalControl(){constopenModal=()=>{createModal(<Modal/>).then((data)=>{alert(data);});};return(<div><button>Open</button></div>);}Managed modals
"use client";functionManagedModal({ text }: {text: string}){const{ key, stackIndex, hide, resolve }=managedModal.useModal();return(<div><div>
This is managed modal {key} at {stackIndex}<br/>{text}</div><buttononClick={()=>{// typed resolve paramsresolve(`managed modal ${key} resolved`);hide();}}>
close
</button></div>);}exportconstmanagedModal=createManagedModal<string,// typescript generic inference is all or nothing, so we have to manually specify the props typeReact.ComponentProps<typeofManagedModal>>(ManagedModal);functionModalControl(){// by default, if same modal is opened multiple times, the previous modal will be closed, and resolve() will only resolve current modal instance.// you can optionally override this behavior.constopenManagedModal=()=>{// typed resolve paramsmanagedModal.open({text: `hello ${Math.random()}`}).then((data)=>{alert(data);});};return(<div><buttononClick={openManagedModal}>Open Managed</button></div>);}Clone the repo and run the example Next.JS app.
- Add testings
- Add JSDoc