Applying React.js CSS Transitions on initial render

In this post I’m going to explore a quick solution to applying React.js CSS transitions to components when they are first rendered to the DOM by creating an interface component for the ReactCSSTransitionGroup add-on.

What does the ReactCSSTransitionGroup add-on do?

The ReactCSSTransitionGroup is an add-on component for React.js which allows you to perform CSS transitions and animations when a React component enters or leaves the DOM. When a new item is added to a ReactCSSTransitionGroup it receives CSS classes to initiate an enter transition, and when it leaves the DOM it receives the corresponding classes to trigger a leaving transition.

This essentially allows you to apply whatever CSS transitions you want to elements as they enter and leave the DOM.

The problem

Recently while using the ReactCSSTransitionGroup add-on for React.js, I ran into the issue of not being able to apply the enter transitions to the child elements when the component is first rendered to the DOM.

When the component is initially rendered, both the ReactCSSTransitionGroup and all of its child elements appear in the DOM at the same time. However, the ReactCSSTransitionGroup will only apply the appropriate animation classes to any child elements which enter the DOM after the initial render.

To illustrate what I mean, here is the example from the React.js documentation implemented in CodePen.

The components inside the transition group will transition in and out of the page when added or removed as defined by the corresponding css rules, but when you first load the page they do not transition in on the initial render.

A work-around to achieve the desired effect

One way around this is to delay the rendering of child components until after the parent component has mounted to the DOM. This approach is outlined in this StackOverflow answer. This is a pretty simple, yet effective, work-around, and requires adding the following code to the component which is responsible for rendering the ReactCSSTransitionGroup component.


    getInitialState: function() {
        return { mounted: false };
    },
    componentDidMount: function() {
        this.setState({ mounted: true });
    },
    render: function() {
        var child;
        if(this.state.mounted){
            child = (<div> ... </div>);
        }
        return (
            <ReactTransitionGroup transitionName="example">
                {child}
            </ReactTransitionGroup>
        );
    }

How can this solution be improved?

The conditional re-render after mounting is a fine work around, but if I want to use this effect in multiple components then I’m going to be repeating it often. Furthermore, I don’t particularly like the idea polluting the component state with an otherwise needless property.

Ideally I’d like a solution which doesn’t affect my component state, and doesn’t result in repeated code.

Creating an interface component for ReactCSSTransitionGroup

To achieve this, I created an interface component for the ReactCSSTransitionGroup component. It’s usage is identical to the ReactCSSTransitionGroup, however it has an additional prop called transitionAppear which takes a boolean value. This prop will handle the conditional rendering based on mounted state, then simply wraps the elements you want to animate with the regular ReactCSSTransitionGroup add-on component.


    var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;

    var ReactCSSTransitionGroupAppear = React.createClass({

        propTypes: {
            transitionName: React.PropTypes.string.isRequired,
            transitionEnter: React.PropTypes.bool,
            transitionLeave: React.PropTypes.bool,
            transitionAppear: React.PropTypes.bool
        },

        getInitialState: function() {
            return {mounted: false}
        },

        getDefaultProps: function() {
            return {
                transitionEnter: true,
                transitionLeave: true,
                transitionAppear: true
            };
        },

        componentDidMount: function() {
            this.setState({ mounted: true });
        },

        render: function (){

            var children;

            if(!this.props.transitionAppear){
                children = this.props.children;
            }
            else{
                if(this.state.mounted){
                    children = this.props.children;
                }
            }

            return(
                <ReactCSSTransitionGroup
                    transitionName={this.props.transitionName}
                    transitionEnter={this.props.transitionEnter}
                    transitionLeave={this.props.transitionLeave}
                >
                    {children}
                </ReactCSSTransitionGroup>
            )
        }
    })

To use this component, I simply include it in my app with the rest of my components and use it in place of the ReactCSSTransitionGroup add-on. Here is a codePen using it in place of the ReactCSSTransitionGroup from the example in the React.js documentation.

Conclusion

While this still obviously isn’t an ideal solution, I think it does provide a cleaner implementation of the most common work-around.

It seems like the ability to apply a transition when the component first appears should be available in the ReactCSSTransitionGroup component soon. There is a pull request on Facebook’s React.js GitHub repo which implements the functionality and it was merged in to the master branch last November. It has yet to make it in to a stable release, but hopefully it’s not too far away. In the meantime, if you’d like to use the wrapper component you can find it on my GitHub page.

Please leave a comment if you have any questions, feedback or suggestions!

11 thoughts on “Applying React.js CSS Transitions on initial render”

  1. Nice article! I had this problem with ReactCSSTransitionGroup and I thought it was an issue with my component. Thanks for clarifying this and presenting an elegant solution!

  2. Nice solution, my only question would be why not just use the normal ReactCSSTransitionGroup component if you don’t need the components to transition on initial render? That way you could remove the transitionAppear prop & subsequent logic from the ReactCSSTransitionGroupAppear component and have it always run the transitions on initial render.

  3. Thanks for the help John.

    In your first code example I think you meant getInitialState and not setInitialState 🙂

  4. The solution is too complex, I do following

    componentWillMount sets the initialRender flag
    componentDidMount resets the initialRender flag

    render has the following code
    var items = this.initialRender ? [] : this.props.realData;

    After the initial render is done, force re-ender by calling
    setTimeout( function() {self.forceUpdate();}, 0) from componentDidMount

    The idea is following
    1) First render renders empty item list
    2) The second render is scheduled immediatly after componentDidMount

    • Hi Anton, thanks for your comment.

      Your solution is similar to the one outlined in the Stack Overflow article I linked to – which as I mentioned in the article is a perfectly acceptable solution – but the goal for the solution I proposed is to have a component you can use identically to the regular CSSTransitionGroup without repeating code each time you need to perform a transition on initial render.

  5. Hey John, thank you for your great blogpost!

    Since version 0.13 ReactCSSTransitionGroup supports the prop transitionAppear.

    TransitionAppear expects a boolean and set true will add the CSS classes example-appear and example-appear-active during the initial mount phase.

    
      Fading at Initial Mount
    
    
    .example-appear {
      opacity: 0.01;
      transition: all .3s ease-in;
    }
    
    .example-appear.example-appear-active {
      opacity: 1;
    }

    I just created a pull request with updates for the documentation. https://github.com/facebook/react/pull/3837

Comments are closed.