A Vue Modal Manager (via Vuex)

Keagan Chisnall
4 min readFeb 7, 2020
Photo by Austin Distel on Unsplash

I have a love/hate relationship with modals. I love how they:
- Focus users on a task
- Don’t disorientate users as much as a new page
- Let users see that their data/information is still there (in the background)
- Can hide relevant (but maybe unnecessary-for-this-session) content/forms until they’re needed

BUT! I find them annoying to manage.

Final code and app is at the bottom 😉

Issues and Traditional Solutions

Historically, I’ve made a modal wrapper component around my modals with props such as open, and emit events such as close which are pretty self explanatory.

This works great until you need multiple locations to open that modal (e.g. contact form). Because it relies on props and emit, you can either:

  • Just include another instance of that component where you need it — meaning you must duplicate the open prop and close handler in each location
  • Use an event bus or store (Vuex) to manage the modal open state and handle a close event — But you then need to create store records for each modal (states/getters/mutations/actions)

But recently I wondered if I could just have a modal manager which didn’t need hardcoding in either component data/methods or Vuex states/getters/mutations/actions…

My Alternative

The Store

The best way I could think of achieving this was a Vuex modal store, which tracked which modal was open, and could close that modal if needed; but could manage the modals by a property of the modal itself — I chose name.

My namespaced (“modals”) store

It’s pretty much just an array (open) state which can have names added to or removed from via open() and close() actions.
Side note: When I add a name to the open array I use unshift to make sure it’s at the beginning, and when I return the “active” modal I get the first element in the array. This saves me from mucking about with array lengths etc.

The Wrapper Component

The modal template is also relatively standard:
- Its content is only created if isOpen
- It’s made up of a clickable overlay which can close the modal
- It also has a close button
- It has a slot for content

A few extra points: The overlay is hidden if it’s not active (stops the window getting darker for every modal opened); I use click.self (so you can still interact with the content without it closing); and the content slot makes the close() method accessible as a property of the slot scope (if needed by the user).

The script is equally simple with a few things to note:
- It requires a name prop
- It gets isActive and isOpen from the store
- It closes through the store
- It also has a backup close call if its parent is unexpectedly removed (beforeDestroy())

An example component

Here is a simple reusable component using this wrapper component.

Notice no close / open functions. Just a name prop for the wrapper component (“simple).

Now we can include the Simple Modal somewhere in our app along with a trigger (a button in this case).

We then connect that trigger to dispatch the store open call using the name we gave it when we made it (“simple”).

And voila. Simple modal.

So what?

OK, so I can hear you saying that that’s a lot of effort for a modal — And I agree that’s overkill for one modal. But now that the manager is in place, you can very easily add more modals from anywhere and open them from anywhere. The only thing they need is a unique name to call to open them (they can close themselves).

But something else is now available: You can have modals create modals (even recursively). If you check out my code, I made a “NamedModal” which recursively makes children of itself (appending a random number to its child name) as a new modal every time you press a button; and then you close them in the reverse sequence they were opened. All without the need of managing through data properties and close methods.

A few differences my production version includes: $emitted close event (just in case you want to listen to that; obviously prettier and reactive; extra slots for even more customization.

This example has been simple and contrived, but I hope it inspires some thoughts in the area of simpler UI management. If you can think of any considerations, improvements, and alternatives, please share them in the comments 🤟.

--

--

Keagan Chisnall

I have a background in process engineering, management, and consulting; but my passion is making systems and processes more efficient. Visit me: chisnall.io