DEV Community

William Baker
William Baker

Posted on

Advanced Tab Interfaces with react-tabs in React

react-tabs is a flexible and accessible library for creating tab interfaces in React applications. It provides a complete tab system with keyboard navigation, ARIA attributes, and extensive customization options. This guide walks through advanced usage of react-tabs with React, including custom configurations, controlled tabs, and complex tab patterns. This is part 48 of a series on using react-tabs with React.

Prerequisites

Before you begin, make sure you have:

  • Node.js version 14.0 or higher installed
  • npm, yarn, or pnpm package manager
  • A React project (version 16.8 or higher) or create-react-app setup
  • Basic knowledge of React hooks (useState, useCallback)
  • Familiarity with JavaScript/TypeScript
  • Understanding of CSS for styling

Installation

Install react-tabs using your preferred package manager:

npm install react-tabs
Enter fullscreen mode Exit fullscreen mode

Or with yarn:

yarn add react-tabs
Enter fullscreen mode Exit fullscreen mode

Or with pnpm:

pnpm add react-tabs
Enter fullscreen mode Exit fullscreen mode

After installation, your package.json should include:

{
  "dependencies": {
    "react-tabs": "^5.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Project Setup

Import react-tabs styles in your main entry file:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import 'react-tabs/style/react-tabs.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

First Example / Basic Usage

Let's create a simple tab interface. Create a new file src/TabsExample.jsx:

// src/TabsExample.jsx
import React from 'react';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';

function TabsExample() {
  return (
    <div style={{ padding: '20px' }}>
      <h2>Basic Tabs Example</h2>
      <Tabs>
        <TabList>
          <Tab>Tab 1</Tab>
          <Tab>Tab 2</Tab>
          <Tab>Tab 3</Tab>
        </TabList>

        <TabPanel>
          <h3>Content for Tab 1</h3>
          <p>This is the content of the first tab.</p>
        </TabPanel>
        <TabPanel>
          <h3>Content for Tab 2</h3>
          <p>This is the content of the second tab.</p>
        </TabPanel>
        <TabPanel>
          <h3>Content for Tab 3</h3>
          <p>This is the content of the third tab.</p>
        </TabPanel>
      </Tabs>
    </div>
  );
}

export default TabsExample;
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

// src/App.jsx
import React from 'react';
import TabsExample from './TabsExample';
import './App.css';

function App() {
  return (
    <div className="App">
      <TabsExample />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This creates a basic tab interface with three tabs and their corresponding content panels.

Understanding the Basics

react-tabs provides several key components:

  • Tabs: Main container component
  • TabList: Container for tab buttons
  • Tab: Individual tab button
  • TabPanel: Content panel for each tab
  • Controlled mode: Manage tab state externally
  • Keyboard navigation: Built-in arrow key navigation
  • Accessibility: Full ARIA support

Key concepts for advanced usage:

  • Controlled Tabs: Use selectedIndex and onSelect for controlled behavior
  • Default Index: Set initial tab with defaultIndex prop
  • Disabled Tabs: Disable specific tabs with disabled prop
  • Custom Styling: Override default styles with CSS classes
  • Event Handlers: Use onSelect to handle tab changes

Here's an example with controlled tabs and custom styling:

// src/ControlledTabsExample.jsx
import React, { useState } from 'react';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';

function ControlledTabsExample() {
  const [selectedIndex, setSelectedIndex] = useState(0);

  const handleSelect = (index) => {
    setSelectedIndex(index);
    console.log(`Selected tab: ${index}`);
  };

  return (
    <div style={{ padding: '20px' }}>
      <h2>Controlled Tabs Example</h2>
      <Tabs selectedIndex={selectedIndex} onSelect={handleSelect}>
        <TabList>
          <Tab>Home</Tab>
          <Tab>About</Tab>
          <Tab disabled>Disabled</Tab>
          <Tab>Contact</Tab>
        </TabList>

        <TabPanel>
          <h3>Home Content</h3>
          <p>Welcome to the home page.</p>
        </TabPanel>
        <TabPanel>
          <h3>About Content</h3>
          <p>Learn more about us.</p>
        </TabPanel>
        <TabPanel>
          <h3>Disabled Tab</h3>
          <p>This tab is disabled.</p>
        </TabPanel>
        <TabPanel>
          <h3>Contact Content</h3>
          <p>Get in touch with us.</p>
        </TabPanel>
      </Tabs>
      <p style={{ marginTop: '20px' }}>Selected tab index: {selectedIndex}</p>
    </div>
  );
}

export default ControlledTabsExample;
Enter fullscreen mode Exit fullscreen mode

Practical Example / Building Something Real

Let's build a comprehensive tabbed interface with forms, data display, and settings:

// src/AdvancedTabsInterface.jsx
import React, { useState } from 'react';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';

function AdvancedTabsInterface() {
  const [selectedIndex, setSelectedIndex] = useState(0);
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: ''
  });
  const [data, setData] = useState([
    { id: 1, name: 'Item 1', value: 100 },
    { id: 2, name: 'Item 2', value: 200 },
    { id: 3, name: 'Item 3', value: 300 }
  ]);

  const handleFormChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({ ...prev, [name]: value }));
  };

  const handleFormSubmit = (e) => {
    e.preventDefault();
    alert('Form submitted!');
    setFormData({ name: '', email: '', message: '' });
  };

  const handleSelect = (index) => {
    setSelectedIndex(index);
  };

  return (
    <div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
      <h1>Advanced Tabs Interface</h1>
      <Tabs
        selectedIndex={selectedIndex}
        onSelect={handleSelect}
        selectedTabClassName="react-tabs__tab--selected"
        selectedTabPanelClassName="react-tabs__tab-panel--selected"
      >
        <TabList style={{
          display: 'flex',
          borderBottom: '2px solid #ddd',
          marginBottom: '20px'
        }}>
          <Tab style={{
            padding: '10px 20px',
            cursor: 'pointer',
            border: 'none',
            backgroundColor: 'transparent',
            borderBottom: '2px solid transparent',
            marginRight: '10px'
          }}>
            Form
          </Tab>
          <Tab style={{
            padding: '10px 20px',
            cursor: 'pointer',
            border: 'none',
            backgroundColor: 'transparent',
            borderBottom: '2px solid transparent',
            marginRight: '10px'
          }}>
            Data
          </Tab>
          <Tab style={{
            padding: '10px 20px',
            cursor: 'pointer',
            border: 'none',
            backgroundColor: 'transparent',
            borderBottom: '2px solid transparent',
            marginRight: '10px'
          }}>
            Settings
          </Tab>
        </TabList>

        <TabPanel>
          <div style={{
            padding: '20px',
            border: '1px solid #ddd',
            borderRadius: '8px',
            backgroundColor: '#f8f9fa'
          }}>
            <h2>Contact Form</h2>
            <form onSubmit={handleFormSubmit}>
              <div style={{ marginBottom: '16px' }}>
                <label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
                  Name
                </label>
                <input
                  type="text"
                  name="name"
                  value={formData.name}
                  onChange={handleFormChange}
                  style={{
                    width: '100%',
                    padding: '8px',
                    border: '1px solid #ddd',
                    borderRadius: '4px',
                    boxSizing: 'border-box'
                  }}
                />
              </div>
              <div style={{ marginBottom: '16px' }}>
                <label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
                  Email
                </label>
                <input
                  type="email"
                  name="email"
                  value={formData.email}
                  onChange={handleFormChange}
                  style={{
                    width: '100%',
                    padding: '8px',
                    border: '1px solid #ddd',
                    borderRadius: '4px',
                    boxSizing: 'border-box'
                  }}
                />
              </div>
              <div style={{ marginBottom: '16px' }}>
                <label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
                  Message
                </label>
                <textarea
                  name="message"
                  value={formData.message}
                  onChange={handleFormChange}
                  rows={4}
                  style={{
                    width: '100%',
                    padding: '8px',
                    border: '1px solid #ddd',
                    borderRadius: '4px',
                    boxSizing: 'border-box',
                    resize: 'vertical'
                  }}
                />
              </div>
              <button
                type="submit"
                style={{
                  padding: '10px 20px',
                  backgroundColor: '#007bff',
                  color: 'white',
                  border: 'none',
                  borderRadius: '4px',
                  cursor: 'pointer'
                }}
              >
                Submit
              </button>
            </form>
          </div>
        </TabPanel>

        <TabPanel>
          <div style={{
            padding: '20px',
            border: '1px solid #ddd',
            borderRadius: '8px',
            backgroundColor: '#f8f9fa'
          }}>
            <h2>Data Table</h2>
            <table style={{
              width: '100%',
              borderCollapse: 'collapse',
              backgroundColor: 'white',
              borderRadius: '4px',
              overflow: 'hidden'
            }}>
              <thead>
                <tr style={{ backgroundColor: '#007bff', color: 'white' }}>
                  <th style={{ padding: '12px', textAlign: 'left' }}>ID</th>
                  <th style={{ padding: '12px', textAlign: 'left' }}>Name</th>
                  <th style={{ padding: '12px', textAlign: 'left' }}>Value</th>
                </tr>
              </thead>
              <tbody>
                {data.map(item => (
                  <tr key={item.id} style={{ borderBottom: '1px solid #ddd' }}>
                    <td style={{ padding: '12px' }}>{item.id}</td>
                    <td style={{ padding: '12px' }}>{item.name}</td>
                    <td style={{ padding: '12px' }}>${item.value}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </TabPanel>

        <TabPanel>
          <div style={{
            padding: '20px',
            border: '1px solid #ddd',
            borderRadius: '8px',
            backgroundColor: '#f8f9fa'
          }}>
            <h2>Settings</h2>
            <div style={{ marginBottom: '16px' }}>
              <label style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
                <input type="checkbox" />
                <span>Enable notifications</span>
              </label>
            </div>
            <div style={{ marginBottom: '16px' }}>
              <label style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
                <input type="checkbox" />
                <span>Dark mode</span>
              </label>
            </div>
            <div style={{ marginBottom: '16px' }}>
              <label style={{ display: 'block', marginBottom: '4px' }}>
                Language
              </label>
              <select style={{
                width: '100%',
                padding: '8px',
                border: '1px solid #ddd',
                borderRadius: '4px'
              }}>
                <option>English</option>
                <option>Spanish</option>
                <option>French</option>
              </select>
            </div>
            <button
              style={{
                padding: '10px 20px',
                backgroundColor: '#28a745',
                color: 'white',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer'
              }}
            >
              Save Settings
            </button>
          </div>
        </TabPanel>
      </Tabs>
    </div>
  );
}

export default AdvancedTabsInterface;
Enter fullscreen mode Exit fullscreen mode

Add custom styles in App.css:

/* src/App.css */
.react-tabs {
  -webkit-tap-highlight-color: transparent;
}

.react-tabs__tab-list {
  border-bottom: 2px solid #ddd;
  margin: 0 0 20px;
  padding: 0;
}

.react-tabs__tab {
  display: inline-block;
  border: none;
  bottom: -2px;
  position: relative;
  list-style: none;
  padding: 10px 20px;
  cursor: pointer;
  background-color: transparent;
  border-bottom: 2px solid transparent;
  margin-right: 10px;
}

.react-tabs__tab--selected {
  background: transparent;
  border-color: #007bff;
  color: #007bff;
  border-radius: 0;
  border-bottom: 2px solid #007bff;
}

.react-tabs__tab--disabled {
  color: #ccc;
  cursor: not-allowed;
}

.react-tabs__tab:focus {
  outline: none;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.3);
}

.react-tabs__tab-panel {
  display: none;
}

.react-tabs__tab-panel--selected {
  display: block;
}
Enter fullscreen mode Exit fullscreen mode

Update your App.jsx:

// src/App.jsx
import React from 'react';
import AdvancedTabsInterface from './AdvancedTabsInterface';
import './App.css';

function App() {
  return (
    <div className="App">
      <AdvancedTabsInterface />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This example demonstrates:

  • Controlled tabs with state management
  • Form in a tab panel
  • Data table in a tab panel
  • Settings panel in a tab
  • Custom styling
  • Keyboard navigation support

Common Issues / Troubleshooting

  1. Tabs not switching: Make sure you're using TabPanel components for each Tab. The number of Tab components should match the number of TabPanel components.

  2. Styling not applying: Import the CSS file (import 'react-tabs/style/react-tabs.css'). You can also override styles using CSS classes or inline styles.

  3. Controlled tabs not working: When using controlled mode, make sure you're providing both selectedIndex and onSelect props. The onSelect handler should update the state.

  4. Keyboard navigation not working: react-tabs includes keyboard navigation by default. Make sure the tabs are focused. Use Tab key to navigate between tabs and arrow keys to switch tabs.

  5. Disabled tabs still clickable: Use the disabled prop on the Tab component to disable it. Disabled tabs won't respond to clicks or keyboard navigation.

  6. Accessibility warnings: react-tabs includes ARIA attributes by default. If you see accessibility warnings, make sure you're using the components correctly and not overriding ARIA attributes unnecessarily.

Next Steps

Now that you have an advanced understanding of react-tabs:

  • Explore advanced customization options and theming
  • Learn about nested tabs and complex tab structures
  • Implement custom tab components and animations
  • Add tab persistence and URL synchronization
  • Integrate with React Router for navigation
  • Learn about other tab libraries (react-tabs, reach-ui tabs)
  • Check the official repository: https://github.com/reactjs/react-tabs
  • Look for part 49 of this series for more advanced topics

Summary

You've successfully integrated react-tabs into your React application with advanced features including controlled tabs, custom styling, form integration, and comprehensive tab interfaces. react-tabs provides a flexible, accessible solution for creating tab interfaces with full keyboard navigation support.

SEO Keywords

react-tabs
React tab component
react-tabs tutorial
React tab interface
react-tabs installation
React accessible tabs
react-tabs example
React tab navigation
react-tabs setup
React controlled tabs
react-tabs customization
React tab library
react-tabs keyboard navigation
React tab panels
react-tabs getting started

Top comments (0)