Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

Ext JS to React: Tab Panel

Published on March 22, 2018
Last Updated on April 23, 2021
Application Development

This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.

The Tab Panel component is a commonly used view in both desktop and mobile UIs. Using tabbed views maximizes the real estate available for otherwise large, even full-screen layouts. The switchable view effectively offers different “pages” of UI within a given application view. In this article we’ll take a look at a sample implementation of tabs using React to render our views and handle user interactions.

Note: While not a requirement for React, this article’s code examples assume you’re starting with a starter app generated by create-react-app.

React Tab Class

In Ext JS, Tab Panels are passed items with text attributes and / or icons to display in auto-generated Tabs. To accomplish the same thing in React we need to first build our Tab and TabPanel classes. We can then look at an example implementation using both classes along with the CSS needed for styling and layout. Our Tab class will be used internally by our TabPanel class.

import React from 'react';
import FontAwesome from '@fortawesome/react-fontawesome';
import * as Icons from '@fortawesome/fontawesome-free-solid';
import './Tab.css';

const Tab = props => {
  const { tabtext, icon, activetab, cardindex, onClick } = props,
        isActive = activetab === cardindex ? ' active' : '';

  return (
    <div className={`tab${isActive}`} onClick={onClick}>
      {icon ? <FontAwesome icon={Icons[`fa${icon}`]} /> : ''}
      {tabtext}
    </div>
  );
};

export default Tab;

This example requires installing the FontAwesome icon modules:

npm install --save @fortawesome/fontawesome
npm install --save @fortawesome/react-fontawesome
npm install --save @fortawesome/fontawesome-free-solid

Initially, we import React, FontAwesome, and the FontAwesome icons. Next, we’ll extract some variables from the props passed in from the parent TabPanel container (which we’ll define in the next section). This will give us:

  • tabtext as text: the tab text
  • icon (if provided)
  • activetab: the currently active index / tab
  • cardindex: the index where this tab resides
  • The derived isActive state indicating whether the active index matches our tab index. We’ll use this to set an “active” CSS class on our tab to differentiate from inactive tabs
  • onClick: our TabPanel‘s tab-click handler method

Finally, we’ll return the JSX that defines the shape of our tab. This will include the class decoration when the tab is active, the click handler, and the tab text / icon.

React Tab CSS

Below is the CSS imported by the above Tab.js:

.tab {
  text-align: center;
  cursor: pointer;
  border: 2px solid transparent;
}
.tab:hover {
  background-color: #d8d8d8;
}
.tab.active:hover {
  background-color: transparent;
}
.tab .svg-inline--fa {
  margin-right: 8px;
}

React TabPanel Class

Next, let’s look at the TabPanel class:

import React, { Component } from 'react';
import Tab from './Tab';
import './TabPanel.css';

class TabPanel extends Component {
  static defaultProps = {
    activetab: 0,
    className: '',
    position: 'top'
  };

  constructor(props) {
    super(props);

    this.state = {
      activetab: props.activetab
    };
  }

  render() {
    const { children, className, position } = this.props;
    const { activetab } = this.state;

    return (
      <div {...this.props} className={`${className} tab-panel ${position}`}>
        <div className={`tab-strip`}>
          {React.Children.map(children, (child, i) => (
            <Tab
              onClick={this.onTabClick.bind(this, i)}
              {...child.props}
              cardindex={i}
              activetab={activetab}
            />
          ))}
        </div>
        <div className="card-ct">
          {React.Children.map(children, (child, i) => {
            let { className } = child.props;
            className = className ? ` ${className}` : '';

            const isActive = i === activetab ? ' active' : '';
            const cardProps = {
              ...child.props,

              className: `card${className}${isActive}`,
              cardindex: i,
              activetab: activetab
            };

            return React.cloneElement(child, cardProps);
          })}
        </div>
      </div>
    );
  }

  onTabClick(activetab) {
    this.setState({
      activetab
    });
  }
}

export default TabPanel;

React TabPanel Class Explained

The static defaultProps property sets the defaults for various props on the TabPanel. The constructor method sets the initial state and activetab property. The onTabClick method handles the Tab click that sets the activetab property that decorates the active tab and shows the active child card. The render method:

  • Combines any className string passed in with those added by the class
  • Collects the activetab from the component state object to inform the tabs / cards which one is currently active / visible
  • In the return:
    • Create the wrapping TabPanel element that will house the tab container and card container in a flex layout
    • The tab container is added and we iterate over the child nodes (cards) passed to our TabPanel to create Tab instances passing in the child nodes’ text and icon along with the current node index and the active tab index from the TabPanel‘s state. The active tab is decorated as active when the cardindex matches the activetab.
    • The child node (card) container is added and we loop over the child nodes, this time calling React.cloneElement in order to add a few props like className, cardindex, and activetab to the original nodes that were passed in. The cloned elements are returned in an array as the child nodes of the parent element. The card whose cardindex matches the activetab is shown while the other cards are hidden using CSS rules.

React TabPanel CSS

Before we look at implementing our TabPanel, let’s first show the CSS used to make our TabPanel lay out correctly and toggle active tabs / cards:

.tab-panel {
  display: flex;
}
.tab-panel.top {
  flex-direction: column;
}
.tab-panel.bottom {
  flex-direction: column-reverse;
}
.tab-panel.left {
  flex-direction: row;
}
.tab-panel.right {
  flex-direction: row-reverse;
}
 
.tab-strip {
  background-color: #e8e8e8;
  display: flex;
  justify-content: flex-start;
}
.tab-strip.indicator {
  justify-content: center;
}
.tab-panel.top .tab-strip,
.tab-panel.bottom .tab-strip {
  flex-direction: row;
}
.tab-panel.left .tab-strip,
.tab-panel.right .tab-strip {
  flex-direction: column;
}

.tab-panel.top .tab,
.tab-panel.bottom .tab {
  padding: 12px 6px;
}
.tab-panel.left .tab,
.tab-panel.right .tab {
  padding: 6px 12px;
}
.tab-panel.top .tab.active {
  border-bottom-color: #1e8bfb;
}
.tab-panel.left .tab.active {
  border-right-color: #1e8bfb;
}
.tab-panel.bottom .tab.active {
  border-top-color: #1e8bfb;
}
.tab-panel.right .tab.active {
  border-left-color: #1e8bfb;
}
 
.card-ct {
  flex: 1;
  display: flex;
} 
.card {
  padding: 12px;
  flex: 1 0 auto;
  display: none;
}
.card.active {
  display: block !important;
}

React TabPanel Example

Below is the code used to create a TabPanel with two tabs / cards each with tab text + icon:

import React, { Component } from 'react';
import TabPanel from './TabPanel';

class App extends Component {
  render() {
    return (
      <TabPanel style={{ height: '400px', width: '600px' }}>
        <div tabtext="Home" icon="Home">
          Content for the first panel
        </div>
        <div tabtext="User" icon="User">
          ... and the second panel
        </div>
      </TabPanel>
    );
  }
}

export default App;
Ext JS to React: Tab Panel 1
Ext JS to React: Tab Panel 2

In our instance example, we’ve passed in a style prop to give the TabPanel specific dimensions. The activetab index may be passed in as a prop (defaults to 0). Another available prop is `position` that orients the tabs on one of the four TabPanel‘s edges (default is “top”). Our TabPanel class appends the position passed (top, left, right, and bottom) to the className of the TabPanel‘s outer element. The CSS rules then position the tabs accordingly using flexbox.

Conclusion

There are a number of pre-built UI solutions that exist in the React community. A few worth mentioning are Material UI, Semantic, and Kendo UI. If you’d like to animate and / or swipe cards within your TabPanel be sure to check out the code in the Swipeable Example on Material UI’s tab component page. In our next blog article we’ll take a look at the carousel whose utility closely resembles that of the tab panel, but with a bit leaner navigation UI.

Posted in Application Development
Share this

Seth Lemmons

Seth Lemmons is a Senior Front End Engineer at Modus Create. Seth has devoted several years to learning Ext JS. He has spent the last 10 years building and supporting web applications with an eye for excellent user experience. Outside of work and a young family Seth has very little free time to just do what he wants. But, if he did have some extra time he'd kinda be into learning vector illustration. And someday he hopes to play video games again.
Follow

Related Posts

  • React Landing
    Ext JS to React: Migration to Open Source

    Worried about Migrating from Ext JS? Modus has the Answers Idera’s acquisition of Sencha has…

  • Ext JS to React: FAQ
    Ext JS to React: FAQ

    This is part of the Ext JS to React blog series. React is Facebook's breakout…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers
  • Let’s talk
  • EN
  • FR