A year ago when I was migrating our Backbone code into React, there were many problems. One of them was the building the modals. Bootstrap was nice, but not fitting into the way React is working, surely I was going to make my own.

Building modals is simple at first, apparently. Append a <div> tag to the body, centre the card, it looks OK. But very quickly I found centring is not easy without the help of Flexbox and back that time we don't use it in our production. Anyway, make the modal work and it's done.

First modal called react-lite-modal

It also becomes hard when my boss requested me to add animations. Yeah, there's transitionGroup addon, it's enough for adding animations. But how about the performance? In order to make CSS transition work, I need a <div> tag as container so transition will start there. But it resulted in tens of tags added to the body. To polish the performance, I made some tricks to reuse some of the container tags, and finally fixed bugs and release our new modal.

Here is the modal I got back that time: https://github.com/jianliaoim/react-lite-layered

New techniques is hard! Well, old techs are tedious and slow. Both make people sad.

I have to admit there's still some performance issue since they are actually rendered to detect during rendering. There's quite some computations even modals and popovers are invisible. Well, tens of modals and popovers are invisible, when they are not visible.

And then routed-modal

After creating the component router-view I got some new ideas of dealing with UI things. You see, router is corresponding to a piece of data which is shared globally in an app. And the conclusion is: that data representing a router is more important than the router itself. We can always changes the ways we render the router, or I would say, the address bar. We may choose rendering it with Chrome's address bar, or Firefox's, just name it as you like.

Router can be controlled by a piece of data in the store. Modals are very like routers, they are always taking up a whole screen. Why can't I put the modal that represents the modal in the Store and manage the modal just like the way I manage components and routed pages?

After some discussions with my teammate I started to try it. First, it was drafted in CirruScript which is my own taste of syntax of JavaScript. I finished making it even without transitionGroup addon.


Well, there was quite some drawbacks: first, the content is unmounted as soon as it begins to close; second, it's based on CSS property pointer-events which is not support in old IE; third, solution for popovers not included.

Now called react-stack-modal

Later I rewrote that modal in CoffeeScript and fixed that 3 issues I mention. It look good. Very easily I implemented rules to make closing actions more predictable the it ever was:


The main idea of stack-modal is to manage modals and popovers in a List provided by immutable-js:

modalStack = [
  type: 'modal', name: 'demo-1', data: {}
  type: 'modal', name: 'demo-2', data: {}
  type: 'popover', name: 'demo-3', data: {}

Every time there's an action to open of close a modal, I can just read modalStack in detail and decide the next state of modals are. And it appear very performant since no tag is needed to be rendered when modal is not there. And the transition looks good.

The problem of copied state

By doing this, modals are actually decouple from the component from where it's opened. Arguments of current modal is copied as the data field of one item in modalStack. It's worked very nicely for page-like components.

But for components that are actually not decoupled, no, it's a bad idea. The popovers opened in such way will never send data back as usual, and new popover does not render as the private state changes. As a repair, we could add an event emitter which redirect data changes to another component in another branch, in some cases. I don't think this is a good fix though.

So, it's unsolved. I really like my new modal solution(popover included), but for the reason above I can't replace my old ones with it. Feeling bad.

Write your comment…

This is good, but I was planning to use Modals from react-bootstrap. How would you compare the two? Any reasons why I should choose react-stack-modal over react-bootstrap's modal?

I don't think it's good idea to replace Bootstrap Modal with stack-modal but I can talk about the pros and cons:

Bootstrap Modal is inserting DOM inside that <App> component. So it renders the virtual DOM of the modal every time there's an update. Also there's a <noscript> leaving inside the parent component.

stack-modal is moving modal totally to the top level of the DOM tree. Not only the DOM is mounted at top level, the data that represents the Modals is saved in Store, not in a component state. So no overhead of modal rendering if there's no modal. Suppose there's a list of things that need modals, there would be a big difference.

And bad part of stack-modal is 1) it's also totally decoupled from the component which opened it, there's no simple way to send event back or call a passed function, 2) there's an unsolved issue, "forced layout" during entering transition will break the whole CSS transition, probably a CSS issue but requires a requestAnimationFrame to fix.

Reply to this…