Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to build a settings page in React

Building a settings page is essential for applications that allow users to customize their experience and manage preferences. As the creator of CoreUI with over 10 years of React experience since 2014, I’ve built settings interfaces for applications ranging from simple user preferences to complex enterprise configuration panels. The most effective approach organizes settings into logical sections, uses controlled components for all inputs, and persists changes to localStorage or an API. This pattern provides a professional, user-friendly settings interface.

Create a settings page with organized sections using state management.

import { useState, useEffect } from 'react'

function SettingsPage() {
  const [settings, setSettings] = useState({
    notifications: {
      email: true,
      push: false,
      sms: false
    },
    privacy: {
      profileVisible: true,
      showEmail: false
    },
    appearance: {
      theme: 'light',
      language: 'en'
    }
  })

  useEffect(() => {
    const saved = localStorage.getItem('settings')
    if (saved) {
      setSettings(JSON.parse(saved))
    }
  }, [])

  const handleSave = () => {
    localStorage.setItem('settings', JSON.stringify(settings))
    alert('Settings saved!')
  }

  return (
    <div className="settings-page">
      <h1>Settings</h1>
      <NotificationSettings settings={settings} setSettings={setSettings} />
      <PrivacySettings settings={settings} setSettings={setSettings} />
      <AppearanceSettings settings={settings} setSettings={setSettings} />
      <button onClick={handleSave}>Save Settings</button>
    </div>
  )
}

The settings state uses nested objects to organize related preferences. The useEffect loads saved settings from localStorage on mount. The handleSave function persists settings. Splitting settings into separate components keeps code organized.

Creating Toggle Switches for Boolean Settings

Build reusable toggle components for on/off preferences.

function NotificationSettings({ settings, setSettings }) {
  const handleToggle = (key) => {
    setSettings({
      ...settings,
      notifications: {
        ...settings.notifications,
        [key]: !settings.notifications[key]
      }
    })
  }

  return (
    <section className="settings-section">
      <h2>Notifications</h2>
      <div className="setting-item">
        <label>
          <span>Email Notifications</span>
          <input
            type="checkbox"
            checked={settings.notifications.email}
            onChange={() => handleToggle('email')}
          />
        </label>
      </div>
      <div className="setting-item">
        <label>
          <span>Push Notifications</span>
          <input
            type="checkbox"
            checked={settings.notifications.push}
            onChange={() => handleToggle('push')}
          />
        </label>
      </div>
      <div className="setting-item">
        <label>
          <span>SMS Notifications</span>
          <input
            type="checkbox"
            checked={settings.notifications.sms}
            onChange={() => handleToggle('sms')}
          />
        </label>
      </div>
    </section>
  )
}

The handleToggle function updates individual notification settings while preserving others. The spread operators ensure immutability. Each checkbox is controlled by the settings state. This pattern makes it easy to add or remove settings.

Creating Dropdown Selectors

Implement dropdown selectors for settings with multiple options.

function AppearanceSettings({ settings, setSettings }) {
  const handleThemeChange = (e) => {
    setSettings({
      ...settings,
      appearance: {
        ...settings.appearance,
        theme: e.target.value
      }
    })
  }

  const handleLanguageChange = (e) => {
    setSettings({
      ...settings,
      appearance: {
        ...settings.appearance,
        language: e.target.value
      }
    })
  }

  return (
    <section className="settings-section">
      <h2>Appearance</h2>
      <div className="setting-item">
        <label>
          <span>Theme</span>
          <select value={settings.appearance.theme} onChange={handleThemeChange}>
            <option value="light">Light</option>
            <option value="dark">Dark</option>
            <option value="auto">Auto</option>
          </select>
        </label>
      </div>
      <div className="setting-item">
        <label>
          <span>Language</span>
          <select value={settings.appearance.language} onChange={handleLanguageChange}>
            <option value="en">English</option>
            <option value="es">Spanish</option>
            <option value="fr">French</option>
            <option value="de">German</option>
          </select>
        </label>
      </div>
    </section>
  )
}

Each select element is controlled by the settings state. The change handlers update specific appearance settings. The value prop ensures the select displays the current setting. This provides immediate visual feedback.

Creating Text Input Settings

Add text input fields for customizable text settings.

import { useState } from 'react'

function ProfileSettings({ settings, setSettings }) {
  const [displayName, setDisplayName] = useState(settings.profile?.displayName || '')
  const [bio, setBio] = useState(settings.profile?.bio || '')

  const handleSaveProfile = () => {
    setSettings({
      ...settings,
      profile: {
        displayName,
        bio
      }
    })
  }

  return (
    <section className="settings-section">
      <h2>Profile</h2>
      <div className="setting-item">
        <label>
          <span>Display Name</span>
          <input
            type="text"
            value={displayName}
            onChange={(e) => setDisplayName(e.target.value)}
            placeholder="Enter your display name"
          />
        </label>
      </div>
      <div className="setting-item">
        <label>
          <span>Bio</span>
          <textarea
            value={bio}
            onChange={(e) => setBio(e.target.value)}
            placeholder="Tell us about yourself"
            rows="4"
          />
        </label>
      </div>
      <button onClick={handleSaveProfile}>Update Profile</button>
    </section>
  )
}

Local state (displayName, bio) tracks temporary input values. The handleSaveProfile function commits changes to the main settings state. This prevents updating global state on every keystroke, improving performance.

Implementing Settings Tabs

Organize many settings into tabs for better navigation.

import { useState } from 'react'

function TabbedSettings() {
  const [activeTab, setActiveTab] = useState('general')
  const [settings, setSettings] = useState({})

  return (
    <div className="settings-page">
      <div className="tabs">
        <button
          className={activeTab === 'general' ? 'active' : ''}
          onClick={() => setActiveTab('general')}
        >
          General
        </button>
        <button
          className={activeTab === 'notifications' ? 'active' : ''}
          onClick={() => setActiveTab('notifications')}
        >
          Notifications
        </button>
        <button
          className={activeTab === 'privacy' ? 'active' : ''}
          onClick={() => setActiveTab('privacy')}
        >
          Privacy
        </button>
        <button
          className={activeTab === 'appearance' ? 'active' : ''}
          onClick={() => setActiveTab('appearance')}
        >
          Appearance
        </button>
      </div>
      <div className="tab-content">
        {activeTab === 'general' && <GeneralSettings settings={settings} setSettings={setSettings} />}
        {activeTab === 'notifications' && <NotificationSettings settings={settings} setSettings={setSettings} />}
        {activeTab === 'privacy' && <PrivacySettings settings={settings} setSettings={setSettings} />}
        {activeTab === 'appearance' && <AppearanceSettings settings={settings} setSettings={setSettings} />}
      </div>
    </div>
  )
}

The activeTab state controls which settings section is visible. Each button updates the active tab. This keeps the interface clean when there are many settings categories. Only the active tab’s components render, improving performance.

Syncing Settings with Backend API

Persist settings to a server instead of localStorage.

import { useState, useEffect } from 'react'

function SettingsWithAPI() {
  const [settings, setSettings] = useState(null)
  const [loading, setLoading] = useState(true)
  const [saving, setSaving] = useState(false)

  useEffect(() => {
    fetchSettings()
  }, [])

  const fetchSettings = async () => {
    try {
      const response = await fetch('/api/settings')
      const data = await response.json()
      setSettings(data)
    } catch (error) {
      console.error('Failed to load settings:', error)
    } finally {
      setLoading(false)
    }
  }

  const saveSettings = async () => {
    setSaving(true)
    try {
      const response = await fetch('/api/settings', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(settings)
      })
      if (response.ok) {
        alert('Settings saved successfully!')
      }
    } catch (error) {
      console.error('Failed to save settings:', error)
      alert('Failed to save settings')
    } finally {
      setSaving(false)
    }
  }

  if (loading) return <div>Loading settings...</div>
  if (!settings) return <div>Failed to load settings</div>

  return (
    <div>
      {/* Settings sections */}
      <button onClick={saveSettings} disabled={saving}>
        {saving ? 'Saving...' : 'Save Settings'}
      </button>
    </div>
  )
}

The fetchSettings function loads settings from the API on mount. The saveSettings function persists changes to the server. The saving state disables the save button during API calls. This prevents duplicate requests and provides user feedback.

Best Practice Note

This is the same settings page architecture we use in CoreUI admin templates for user preference management. For production applications, implement autosave with debouncing to persist changes automatically without requiring manual saves. Add unsaved changes warnings before navigation. Group related settings visually with section headers or cards. Consider using a form library like React Hook Form for complex validation requirements. For enterprise applications with role-based access, conditionally render settings based on user permissions. Check out CoreUI’s form components for professionally styled switches, inputs, and selects that work great in settings interfaces.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author