Skip to main content
Show is a React component that conditionally renders children based on an observable condition, with support for else fallback and value passing.

Usage

import { Show } from '@legendapp/state/react'
import { state$ } from './state'

function Component() {
  return (
    <Show if={state$.isLoggedIn}>
      <UserDashboard />
    </Show>
  )
}

Signature

function Show<T>(props: {
  if?: Selector<T>
  ifReady?: Selector<T>
  else?: ReactNode | (() => ReactNode)
  $value?: Observable<T>
  wrap?: FC<{ children: ReactNode }>
  children: ReactNode | ((value?: T) => ReactNode)
}): ReactElement

Props

if
Selector<T>
A selector or observable whose value determines whether to show the children. Shows children when the value is truthy.Cannot be used together with ifReady.
ifReady
Selector<T>
Similar to if, but specifically checks if the observable value is “ready” (not undefined, not a pending Promise). Useful for async observables.Cannot be used together with if.
else
ReactNode | (() => ReactNode)
Content to render when the condition is false. Can be:
  • A React node (JSX)
  • A function that returns a React node
If omitted, nothing is rendered when the condition is false.
$value
Observable<T>
An observable to pass to the children function. When provided, the children function receives $value.get() instead of the condition value.
wrap
FC<{ children: ReactNode }>
A wrapper component to wrap the rendered children. Useful for adding context providers or layout components.
children
ReactNode | ((value?: T) => ReactNode)
required
The content to render when the condition is truthy. Can be:
  • A React node (JSX)
  • A function that receives the condition value and returns a React node

Returns

ReactElement
ReactElement
The rendered result based on the condition - either the children, the else content, or the wrapped children.

Examples

Basic conditional rendering

<Show if={state$.isLoggedIn}>
  <div>Welcome back!</div>
</Show>

With else clause

<Show
  if={state$.isLoggedIn}
  else={<div>Please log in</div>}
>
  <div>Welcome back!</div>
</Show>

With else function

<Show
  if={state$.hasData}
  else={() => {
    console.log('No data available')
    return <EmptyState />
  }}
>
  <DataDisplay />
</Show>

With children function

<Show if={state$.currentUser}>
  {(user) => (
    <div>
      <h1>Hello, {user.name}!</h1>
      <p>Email: {user.email}</p>
    </div>
  )}
</Show>

Check if observable is ready

// Shows loading until data is loaded
<Show
  ifReady={state$.userData}
  else={<Spinner />}
>
  {(data) => <UserProfile data={data} />}
</Show>

With custom value observable

<Show
  if={state$.selectedId}
  $value={state$.items.find(item => item.id === state$.selectedId.get())}
>
  {(item) => <ItemDetails item={item} />}
</Show>

With wrapper component

const ErrorBoundary = ({ children }) => (
  <div className="error-boundary">{children}</div>
)

<Show
  if={state$.hasError}
  wrap={ErrorBoundary}
>
  <ErrorMessage />
</Show>

Complex conditions

<Show if={() => state$.user.age.get() >= 18}>
  <AdultContent />
</Show>

Nested Show components

<Show if={state$.isLoggedIn}>
  <Show if={state$.isPremium} else={<FreeTierBanner />}>
    <PremiumFeatures />
  </Show>
</Show>

With complex else content

<Show
  if={state$.items.length > 0}
  else={
    <div>
      <h2>No items found</h2>
      <button onClick={loadItems}>Load Items</button>
    </div>
  }
>
  {() => (
    <div>
      {state$.items.get().map(item => (
        <ItemCard key={item.id} item={item} />
      ))}
    </div>
  )}
</Show>

Loading states with ifReady

const data$ = observable(async () => {
  const response = await fetch('/api/data')
  return response.json()
})

<Show
  ifReady={data$}
  else={<LoadingSpinner />}
>
  {(data) => <DataTable data={data} />}
</Show>

Behavior

Conditional tracking

Only the active branch (children or else) is tracked and rendered:
<Show if={state$.showAdvanced}>
  {/* These observables are only tracked when showAdvanced is true */}
  <AdvancedSettings config={state$.advancedConfig.get()} />
</Show>

Value passing

When children is a function:
  • Without $value: Receives the condition value
  • With $value: Receives the $value observable’s value
// Receives condition value
<Show if={state$.user}>
  {(user) => <div>{user.name}</div>}
</Show>

// Receives $value
<Show if={state$.userId} $value={state$.users[state$.userId.get()]}>
  {(user) => <div>{user.name}</div>}
</Show>

ifReady behavior

ifReady uses isObservableValueReady() internally to check if the value is:
  • Not undefined
  • Not a pending Promise
  • Actually has a value
This is particularly useful for async observables:
const asyncData$ = observable(async () => fetchData())

// Shows else until Promise resolves
<Show ifReady={asyncData$} else={<Loading />}>
  {(data) => <Display data={data} />}
</Show>

Wrapper usage

The wrap prop is useful for providing context or layout:
const AuthProvider = ({ children }) => (
  <AuthContext.Provider value={authService}>
    {children}
  </AuthContext.Provider>
)

<Show if={state$.requiresAuth} wrap={AuthProvider}>
  <ProtectedContent />
</Show>

Performance

Show uses useSelector internally with skipCheck: true, which means:
  • Re-renders occur when any tracked observable changes
  • No deep equality checking on return values
  • Efficient for conditional rendering logic

Comparison with JavaScript conditionals

Show component

<Show if={state$.isVisible} else={<Fallback />}>
  <Content />
</Show>

Equivalent JSX

{useSelector(state$.isVisible) ? <Content /> : <Fallback />}

Advantages of Show:

  • More declarative and readable
  • Built-in ifReady for async observables
  • wrap prop for providers/context
  • Children function receives typed value
  • Cleaner syntax for complex conditions

Type Parameters

T
type
The type of the condition value. Inferred from the if or ifReady selector.

Notes

  • Cannot use both if and ifReady props simultaneously
  • else can be a function for lazy evaluation
  • Children function receives undefined as value if $value is not provided and condition is a boolean
  • Wrapper component must accept a children prop
  • All props except children are tracked with useSelector
  • Switch - Multi-case conditional rendering
  • For - List rendering with observables
  • Memo - Memoize computed sections
  • observer - Automatic observable tracking

Build docs developers (and LLMs) love