Create animated sidebar component in React with react-transition-group

Luqman Ahmed

--

An animation is considered to be one of the most used methods to add aesthetics to an app, If you want to change an element from one state to another smoothly, a transition is a good choice.

In this article I will show you how to build a simple navigation bar with transition group in React. Here is a preview of the app we’ll be building:

React Transition Group is a very useful tool for transitions, we can add animation to a React App using an explicit group of components known as the React Transition Group and the reason we need to use react transition group for applying custom CSS transitions cause it’s an easy way to perform animations when a React component enters or leaves the DOM while the regular CSS transitions (animations) can only be applied to components (elements) that are always living in the DOM.

1. Lets Setup a New React Project

The easiest way to get started is with a popular tool called Create React App. First install create-react-app, if you don’t already have it, and then make a new project with it.

npm install -g create-react-app

now let's create a new app

create-react-app react-sidebar

using yarn, let's install the packages for animation and route:

yarn add react-transition-group

Now open the project in your favourite editor and run;

npm start

2. Building the components

Let’s start creating our components. I like the convention of using a components/folder inside of src/, but you can follow whatever convention you like. For this example, we care about two main components: our navigation bar and the remaining content. Create empty Sidebar and Content components and load them into App.

// src/components/Content.jsimport React, { Component } from 'react'export default class Content extends Component {
render() {
return <div className="content-container">
I'm the content!
</div>
}
}

Now create another component for sidebar

// src/components/Sidebar.jsimport React, { Component } from 'react'export default class Sidebar extends Component {
render() {
return <div className="sidebar-container">
I'm the sidebar!
</div>
}
}

We have created our 2 basic components lets load them into app.js

src/components/App.jsimport React, { Component } from 'react'
import Sidebar from './Sidebar'
import Content from './Content'
class App extends Component {
render() {
return (
<div className="app">
<Sidebar />
<Content />
</div>
)
}
}
export default App

Our current app looks like this because we haven't added any links in the sidebar and the content section is empty as well.

Let's add placeholder and css styles for sidebar

// src/components/Sidebar.jsimport React, { Component } from 'react'export default class Sidebar extends Component {
renderSidebar = () => {
return <div className="sidebar">
<div className="sidebar-link">Home</div>
<div className="sidebar-link">About</div>
<div className="sidebar-link">Contact</div>
</div>
}
render() {
return <div className="sidebar-container">
{this.renderSidebar()}
</div>
}
}

3. Injecting dummy content

For the sake of example and to keep the Content component uncluttered i will use a prop called dangerouslySetInnerHtmlto add placeholder content to Content so that page can have some weight. Here’s why you shouldn’t use it in production code.)

// src/components/Content.jsimport React, { Component } from 'react'
import { placeholderContent } from '../placeholderContent'
export default class Content extends Component {
render() {
return <div
className="content-container"
dangerouslySetInnerHTML={{__html: placeholderContent}}
/>
}
}
// src/placeholderContent.js

export const placeholderContent = `
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas leo tortor, elementum vel tempus vel, luctus at elit. Maecenas ac mattis urna. Praesent a elementum ipsum. Praesent laoreet molestie tincidunt. Donec ullamcorper, eros quis porta dapibus, tellus mi gravida est, sagittis posuere velit nulla vitae massa. Suspendisse consectetur, dolor sit amet scelerisque cursus, dolor neque sodales ex, vitae facilisis ante lectus non turpis. Duis mattis porttitor lectus. Aliquam ac pharetra justo. Fusce elementum velit sed mauris scelerisque pretium at sit amet odio.
</p>
`

4. Adding controls functionality

We have almost everything in place so let's come to the functionality, our sidebar needs to be opened and closed, so ill use state in order to do so, we also need to control the open and close state in Sidebar with a clickable icon. I am going to use the icons from react-icons lib but you can use whatever you want.

// src/components/Sidebar.jsimport React, { Component } from 'react'
import SidebarIcon from './SidebarIcon'
export default class Sidebar extends Component {
state = {
isOpen: true
}

renderSidebar = () => {
if (!this.state.isOpen) {
return null
}

return <div className="sidebar">
<div className="sidebar-link">Home</div>
<div className="sidebar-link">About</div>
<div className="sidebar-link">Contact</div>
</div>
}
toggleSidebar = () => {
this.setState(prevState => ({
isOpen: !prevState.isOpen
}))
}
render() {
return <div className="sidebar-container">
{this.renderSidebar()}
<div className="sidebar-icon">
<SidebarIcon
isOpen={this.state.isOpen}
handleClick={this.toggleSidebar}
/>
</div>
</div>
}
}
// src/components/SidebarIcon.js

import React from 'react'
import { FaBars, FaClose } from 'react-icons/lib/fa'
const SidebarIcon = ({handleClick, isOpen}) => {
return <span onClick={handleClick}>
{isOpen ? <FaClose /> : <FaBars/>}
</span>
}
export default SidebarIcon

5. Transition Group

Now we’re ready to add transitions to make the opening/closing of our navigation bar is a little smoother. To do this, I’m going to use react-transition-group (docs).

Upon clicking the navigation bar icon, there will be two transitions:

1) the width of the sidebar bar will shrink/expand
2) the links in the sidebar will fade out/in.

Currently, the Sidebar component controls the isOpen rendering logic (in renderSidebar()), but I’m going to pull this out into its own component called SidebarContent.

// src/components/Sidebar.jsrender() {
return <div className="sidebar-container">
<SidebarContent isOpen={this.state.isOpen} />
<div className="sidebar-icon">
<SidebarIcon
isOpen={this.state.isOpen}
handleClick={this.toggleSidebar}
/>
</div>
</div>
}

All I’ve done is removed the renderSidebar method and moved that logic into its own component called SidebarContent. Now our SidebarContent has some room to breathe so we can add transitions.

I’m going to use the Transition component from react-transition-groupbecause it allows me to describe transition styles through JavaScript objects, but you can also use CSSTransition if you’d rather use CSS. Our Transition will need in (a boolean that toggles transition state) and timeout props, as well as an element that controls styling based on state.

// src/components/SidebarContent.jsimport React, { Component } from 'react'
import { Transition } from 'react-transition-group'
const duration = 1000const sidebarStyle = {
transition: `width ${duration}ms`
}
const sidebarTransitionStyles = {
entering: { width: 0 },
entered: { width: '200px' },
exiting: { width: '200px' },
exited: { width: 0 }
}
const linkStyle = {
transition: `opacity ${duration}ms`
}
const linkTransitionStyles = {
entering: { opacity: 0 },
entered: { opacity: 1 },
exiting: { opacity: 1 },
exited: { opacity: 0 }
}
export default class SidebarContent extends Component {
renderLinks = () => {
return <Transition in={this.props.isOpen} timeout={duration}>
{(state) => (
<div style={{
...linkStyle,
...linkTransitionStyles[state]
}}>
<div className="sidebar-link">Home</div>
<div className="sidebar-link">About</div>
<div className="sidebar-link">Contact</div>
</div>
)}
</Transition>
}

render() {
return <Transition in={this.props.isOpen} timeout={duration}>
{(state) => (
<div className="sidebar" style={{
...sidebarStyle,
...sidebarTransitionStyles[state]
}}>
{this.renderLinks()}
</div>
)}
</Transition>
}
}

There’s a lot going on here, so let’s break it down.

There are two Transitions, both controlled by the same in and timeout props. The sidebar and link styles only differ in the transition properties that they control — sidebar changes width (to achieve the “slide in/out” effect) and the links change opacity (to fade in/out in concert with the sidebar transition) at different points in state.

That’s it! Now go forward and animate components at your leisure.

--

--