Skip to content

Client State vs URL State vs LocalStorage: The Complete Guide

Derick Zr4 min read

I built a product configurator that lost the user's selections on refresh.

Customer picks the color, size, and specs. They refresh the page. Everything resets.

I was storing it all in React state.

Wrong choice. Here's what I learned about where to actually store state.

The Three Types of State

1. Client State (React useState)

  • Lives only in memory while your component is mounted
  • Resets when you refresh the page
  • Perfect for: UI interactions, temporary form data, modal states

2. URL State (Query Parameters)

  • Lives in the browser's address bar
  • Shareable - send the URL to someone else and they see the same state
  • Perfect for: filters, search queries, configuration that should be bookmarkable

3. LocalStorage State

  • Persists across browser sessions on the same device
  • Not shareable - only visible to the current user on their device
  • Perfect for: user preferences, theme settings, draft content

Interactive Examples

Try these demos to see the differences in action:

Example 1: Simple Counter

This counter demonstrates all three approaches. Notice how each behaves differently when you refresh the page or copy the URL.

Key Insights:

  • React State: Fast and simple, but lost on refresh
  • URL State: Shareable and bookmarkable, but updates the browser history
  • LocalStorage: Persists across sessions, but can't be shared

Example 2: Phone Options

On a product page, people pick the specs they want and can share the exact configuration if they want.

Why URL State Wins Here:

  • Customer can share their exact configuration via URL
  • Bookmarkable for later purchase
  • Works across devices if they send themselves the link
  • SEO-friendly if you have different pricing per configuration

Decision Framework

Here's how to choose:

Use React State When:

  • Temporary UI state (modals, dropdowns, form inputs)
  • State only matters while user is actively interacting
  • Performance is critical (no serialization overhead)

Avoid when:

  • You need state to survive page refresh
  • Users might want to bookmark or share the state

Use URL State When:

  • State should be shareable via URL
  • State should be bookmarkable
  • Filters, search queries, or configurations
  • SEO matters for different state combinations

Avoid when:

  • State is personal/private
  • State is complex objects (keep URLs clean)

Use LocalStorage When:

  • User preferences that should persist
  • Draft content or "work in progress" data
  • Theme settings, language preferences
  • Shopping cart contents (until checkout)

Avoid when:

  • State needs to be shared between users
  • State is critical (localStorage can be cleared)

Implementation Patterns

URL State Pattern

const [filters, setFilters] = useUrlState({
  category: 'all',
  priceRange: '0-100'
})
 
// Automatically syncs with ?category=electronics&priceRange=50-200
const [filters, setFilters] = useUrlState({
  category: 'all',
  priceRange: '0-100'
})
 
// Automatically syncs with ?category=electronics&priceRange=50-200

LocalStorage Pattern

const [theme, setTheme] = useLocalStorage('theme', 'light')
 
// Persists across browser sessions
const [theme, setTheme] = useLocalStorage('theme', 'light')
 
// Persists across browser sessions

When to Combine Them

Sometimes you need multiple types:

// URL for shareable filters
const [filters, setFilters] = useUrlState({ category: 'all' })
 
// LocalStorage for user preferences  
const [viewMode, setViewMode] = useLocalStorage('viewMode', 'grid')
 
// React state for UI interactions
const [isModalOpen, setIsModalOpen] = useState(false)
// URL for shareable filters
const [filters, setFilters] = useUrlState({ category: 'all' })
 
// LocalStorage for user preferences  
const [viewMode, setViewMode] = useLocalStorage('viewMode', 'grid')
 
// React state for UI interactions
const [isModalOpen, setIsModalOpen] = useState(false)

Common Mistakes

Putting everything in React state

// Wrong: Lost on refresh, can't be shared
const [productConfig, setProductConfig] = useState({
  color: 'red',
  size: 'large',
  model: 'pro'
})
// Wrong: Lost on refresh, can't be shared
const [productConfig, setProductConfig] = useState({
  color: 'red',
  size: 'large',
  model: 'pro'
})

Putting UI state in URL

// Wrong: Clutters URL, not bookmarkable in meaningful way
const [isModalOpen, setIsModalOpen] = useUrlState('modalOpen', false)
// Wrong: Clutters URL, not bookmarkable in meaningful way
const [isModalOpen, setIsModalOpen] = useUrlState('modalOpen', false)

Putting personal preferences in URL

// Wrong: Not personal, gets shared when URL is copied
const [darkMode, setDarkMode] = useUrlState('darkMode', false)
// Wrong: Not personal, gets shared when URL is copied
const [darkMode, setDarkMode] = useUrlState('darkMode', false)

Key Takeaways

  • React State: Fast, temporary, component-scoped
  • URL State: Shareable, bookmarkable, SEO-friendly
  • LocalStorage: Personal, persistent, device-scoped

Use the right tool for the job. Your users will thank you.

Start with React state. Promote to URL or localStorage only when you need it.