The examples on this page use [Tailwind CSS](https://tailwindcss.com) for styling and [Framer Motion](https://www.framer.com/motion) for animations. These examples all use the [fine-grained-reactivity](../fine-grained-reactivity) components so that the parent component renders only once and all renders are optimized to be as small as possible.

## Persisted global state

This example creates a global state object and persists it to Local Storage. Try changing the username and toggling the sidebar and refreshing - it will restore it to the previous state.

<Sandbox height={680} width={64} options={{ showTabs: true }} deps={{ 'framer-motion': '7.6.6' }} files={{
'/State.js': { active: true, code: `
import { observable } from '@legendapp/state'
import { persistObservable } from '@legendapp/state/persist'
import { ObservablePersistLocalStorage } from '@legendapp/state/persist-plugins/local-storage'

export const State = observable({
  settings: {
    showSidebar: false
  },
  user: {
    profile: {
      name: ''
    }
  }
})

persistObservable(State, {
  local: 'example',
  persistLocal: ObservablePersistLocalStorage,
})

`},
'/App.js': `
import { useRef } from 'react'
import { reactive, useObservable, Reactive } from '@legendapp/state/react'
import { motion } from "framer-motion"
import { State } from './State'

const MotionDiv = reactive(motion.div)

export default function App() {
  const renderCount = ++useRef(0).current

  const animPosition = () => ({
    width: State.settings.showSidebar.get() ? 96 : 0
  })

  return (
    <div className="flex absolute inset-0">
      <MotionDiv
        className="bg-gray-600 text-center pt-2 text-white text-sm"
        $initial={animPosition}
        $animate={animPosition}
      >
        Sidebar
      </MotionDiv>
      <div className="flex-1 p-4">
        <div className="text-gray-500 text-sm pb-4">
          Renders: {renderCount}
        </div>
        <div>Username:</div>
        <Reactive.input
          className={classNameInput}
          $value={State.user.profile.name}
        />
        <div>
          <button
            className="bg-gray-300 rounded-lg px-4 py-2 mt-6"
            onClick={State.settings.showSidebar.toggle}
          >
            Toggle sidebar
          </button>
        </div>
        <div>
          <button
            className="bg-gray-300 rounded-lg px-4 py-2 mt-6"
            onClick={() => location.reload()}
          >
            Refresh
          </button>
        </div>
      </div>
    </div>
  )
}

const classNameInput = "border rounded border-gray-300 px-2 py-1 mt-2"
const classNameError = "text-sm text-red-500 mb-2 h-5 pt-1"
`}} />

## Auto-saving Form

This example uses the [useObservableQuery](../helpers-and-hooks#useobservablequery) hook to create an observable using [TanStack Query](https://tanstack.com/query/) that automatically sends mutations back to the server whenever the observable changes.

It then uses the `Reactive` [two-way binding components](../reactive-props#react) to bind those observable directly to the inputs.

So in effect this binds the inputs directly to your server data.

<Sandbox height={680} width={70} options={{ showTabs: true }} deps={{ '@tanstack/react-query': '4.15.0', 'axios': '1.1.3' }} files={{
  '/debounce.js': `
let timeout = 0
export function debounce(fn, time) {
  clearTimeout(timeout)
  timeout = setTimeout(fn, time)
}
`,
'/App.js': `
import axios from 'axios'
import { useRef } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useObservable, Reactive, Memo } from '@legendapp/state/react'
import { enableReactComponents } from '@legendapp/state/config/enableReactComponents'
import { useObservableQuery } from '@legendapp/state/react-hooks/useObservableQuery'
import { debounce } from './debounce'

enableReactComponents()

const queryClient = new QueryClient()

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Example />
    </QueryClientProvider>
  )
}

function Example() {
  const renderCount = ++useRef(0).current
  const { data } = useObservableQuery(
    {
      queryKey: ["data"],
      queryFn: () =>
        axios.get("https://reqres.in/api/users/1")
          .then((res) => res.data.data),
    },
    {
      mutationFn: (newData) => {
        // Uncomment to actually save
        /*
        debounce(() => {
          axios
            .post("https://reqres.in/api/users/1", newData)
            .then((res) =>
              lastSaved.set(Date.now())
            )
        }, 1000)
        */
        lastSaved.set(Date.now())
      }
    }
  )
  const lastSaved = useObservable(0)

  return (
    <div className="p-4">
      <div className="text-gray-500 text-sm pb-4">
        Renders: {renderCount}
      </div>
      <div>Name:</div>
      <Reactive.input
        className={classNameInput}
        $value={data.first_name}
      />
      <div>Email:</div>
      <Reactive.input
        className={classNameInput}
        $value={data.email}
      />
      <div>
        Last saved: <Memo>{lastSaved}</Memo>
      </div>
    </div>
  )
}

const classNameInput = "border rounded border-gray-300 px-2 py-1 mt-2 mb-4"
`}} />

## Form validating

This example uses [useObserve](../hooks#useobserve) to listen to changes in the form state to update the error messages as you type. It waits for the first click of the Save button for a better user experience.

<Sandbox height={680} width={70} files={{
'/App.js': `
import { useRef } from 'react'
import { useObservable, useObserve, Reactive, Memo, Show } from '@legendapp/state/react'
import { enableReactComponents } from '@legendapp/state/config/enableReactComponents'

enableReactComponents()

export default function App() {
  const renderCount = ++useRef(0).current

  const username = useObservable('')
  const password = useObservable('')
  const usernameError = useObservable('')
  const passwordError = useObservable('')
  const didSave = useObservable(false)
  const successMessage = useObservable('')

  useObserve(() => {
    if (didSave.get()) {
      usernameError.set(username.get().length < 3 ?
        'Username must be > 3 characters' :
        ''
      )

      const pass = password.get()
      passwordError.set(
        pass.length < 10 ?
          'Password must be > 10 characters' :
          !pass.match(/\\d/) ?
            'Password must include a number' :
            ''
      )
    }
  })

  const onClickSave = () => {
    // changing didSave runs useObserve immediately, updating error messages
    didSave.set(true)

    if (!usernameError.get() && !passwordError.get()) {
      console.log('Submit form')
      passwordError.delete();
      successMessage.set('Saved!')
    }
  }

  return (
    <div className="p-4">
      <div className="text-gray-500 text-sm pb-4">
        Renders: {renderCount}
      </div>
      <div>Username:</div>
      <Reactive.input
        className={classNameInput}
        $value={username}
      />
      <div className={classNameError}>
        <Memo>{usernameError}</Memo>
      </div>
      <div>Password:</div>
      <Reactive.input
        type="password"
        className={classNameInput}
        $value={password}
      />
      <div className={classNameError}>
        <Memo>{passwordError}</Memo>
      </div>
      <Show if={successMessage}>
        {() => (
          <div>
            {successMessage.get()}
          </div>
        )}
      </Show>
      <div>
        <button
          className="bg-gray-300 rounded-lg px-4 py-2 mt-6"
          onClick={onClickSave}
        >
          Save
        </button>
      </div>
    </div>
  )
}

const classNameInput = "border rounded border-gray-300 px-2 py-1 mt-2"
const classNameError = "text-sm text-red-500 mb-2 h-5 pt-1"
`}} />

## List of messages

This example uses the [useFetch](../helpers-and-hooks#usefetch) hook to get data from a server as an observable, [useComputed](../hooks#usecomputed) to create a computed observable, and [For](../fine-grained-reactivity#for) to display the array of messages in a high-performance way.

<Sandbox height={680} width={70} files={{
'/App.js': `
import { useRef } from 'react'
import { useComputed, useObservable, useObserve, For, Memo, Reactive, Show } from '@legendapp/state/react'
import { useFetch } from '@legendapp/state/react-hooks/useFetch'
import { enableReactComponents } from '@legendapp/state/config/enableReactComponents'

enableReactComponents()

export default function App() {
  const renderCount = ++useRef(0).current

  // Create profile from fetch promise
  const {
    data: { data: profile },
  } = useFetch('https://reqres.in/api/users/1')

  // Username
  const userName = useComputed(() => {
    const p = profile.get()
    return p ?
        p.first_name + ' ' + p.last_name :
        ''
  })

  // Chat state
  const { messages, currentMessage } = useObservable({
    messages: [],
    currentMessage: ''
  })

  // Button click
  const onClickAdd = () => {
    messages.push({
      id: generateID(),
      text: currentMessage.get(),
    })
    currentMessage.set('')
  }

  return (
    <div className="p-4">
      <div className="text-gray-500 text-sm pb-4">
        Renders: {renderCount}
      </div>
      <Show if={userName} else={<div>Loading...</div>}>
        <div>Chatting with <Memo>{userName}</Memo></div>
      </Show>
      <div className="h-64 p-2 my-3 overflow-auto border border-gray-300 rounded">
        <For each={messages}>{(message) => <div><Memo>{message.text}</Memo></div>}</For>
      </div>
      <div className="flex gap-2">
        <Reactive.input
          className="flex-1 px-2 border border-gray-300 rounded min-w-0"
          placeholder="Enter message"
          $value={currentMessage}
          onKeyDown={e => e.key === 'Enter' && onClickAdd()}
        />
        <button
          className="bg-gray-300 rounded-lg px-4 py-2"
          onClick={onClickAdd}
        >
          Send
        </button>
      </div>
    </div>
  )
}

let nextID = 0
function generateID() {
  return nextID ++
}
`}} />


## Animations with reactive props

This example uses [reactive](../reactive-props#make-your-own) to make a version of `motion.div` with [reactive props](../reactive-props) that can animate using observable values. Animating with reactive props is faster than re-rendering the whole component because when the tracked observable changes it triggers a render of only the `motion.div`, so it doesn't need to re-render the parent or children.

This example also creates a [computed observable](../reactivity#computed) text value from the boolean and renders it directly in JSX, which (under the hood) creates a reactive text element that re-renders itself when it changes.

<Sandbox height={580} width={75} options={{ showTabs: true }} deps={{ 'framer-motion': '7.6.6' }} files={{
'/App.js': `
import { useRef } from 'react'
import { observable } from '@legendapp/state'
import { useComputed, Memo } from '@legendapp/state/react'
import { Toggle } from './Toggle'

const settings = observable({ enabled: false })

export default function App() {
  const renderCount = ++useRef(0).current

  // Computed text value
  const text = useComputed(() =>
    settings.enabled.get() ? 'Yes' : 'No'
  )

  return (
    <div className="absolute inset-0 p-4">
      <div className="text-gray-500 text-sm">
        Renders: {renderCount}
      </div>
      <div className="pt-8 pb-4">
        Enabled: <Memo>{text}</Memo>
      </div>
      <Toggle value={settings.enabled} />
    </div>
  )
}
`,
'/Toggle.js': { active: true, code: `
import { reactive } from '@legendapp/state/react'
import { motion } from "framer-motion"

const MotionDiv = reactive(motion.div)

export function Toggle({ value }) {
  return (
    <MotionDiv
      className="border border-gray-200 rounded-full select-none"
      $animate={() => ({
        backgroundColor: value.get() ? '#6ACB6C' : '#C4D1E3'
      })}
      style={{ width: 64, height: 32 }}
      onClick={value.toggle}
    >
      <MotionDiv
        className="bg-white rounded-full shadow"
        style={{ width: 24, height: 24, marginTop: 3 }}
        $animate={() => ({
          x: value.get() ? 32 : 6
        })}
      />
    </MotionDiv>
  )
}
`}}} />

## Show a modal with multiple pages

This example uses [Show](../fine-grained-reactivity#show) to show/hide a modal based on an observable value, and [Switch](../fine-grained-reactivity#switch) to render the active page in the modal.

<Sandbox height={680} width={64} options={{ showTabs: true }} deps={{ 'framer-motion': '7.6.6' }} files={{
'/App.js': `
import { observable } from '@legendapp/state'
import { Show, useComputed, useObservable } from '@legendapp/state/react'
import { AnimatePresence } from "framer-motion"
import { useRef } from 'react'
import { Modal } from './Modal'

export default function App() {
  const renderCount = ++useRef(0).current

  const showModal = useObservable(false)

  return (
    <div className="absolute inset-0 p-4">
      <div className="text-gray-500 text-sm pb-4">
        Renders: {renderCount}
      </div>
      <button
        className="bg-gray-300 rounded-lg px-4 py-2"
        onClick={showModal.toggle}
      >
        Show modal
      </button>
      <Show if={showModal} wrap={AnimatePresence}>
        {() => <Modal show={showModal} />}
      </Show>
    </div>
  )
}
`,
'/Modal.js': `
import { reactive, useObservable, Switch } from '@legendapp/state/react'
import { motion } from "framer-motion"
import { useRef } from 'react'

const MotionDiv = reactive(motion.div)
const MotionButton$ = reactive(motion.button)

const TransitionBounce = {
  type: 'spring',
  duration: 0.4,
  bounce: 0.3,
}

export function Modal({ show }) {
  const renderCount = ++useRef(0).current
  const page = useObservable(0)

  return (
    <motion.div
      className="absolute inset-0 flex items-center justify-center"
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      <div
        className="absolute inset-0 bg-black/60"
        onClick={() => show.set(false)}
      />
      <motion.div
        className="relative bg-white rounded-xl flex flex-col p-4"
        initial={{ opacity: 0, scale: 0.7, translateY: 40 }}
        animate={{ opacity: 1, scale: 1, translateY: 0 }}
        exit={{ scale: 0.7, opacity: 0 }}
        style={{ width: 240, height: 320 }}
        transition={TransitionBounce}
      >
        <div className="text-gray-500 text-sm">
          Renders: {renderCount}
        </div>
        <div className="flex-1 flex justify-center items-center">
          <Switch value={page}>
            {{
              0: () => <div>First Page</div>,
              1: () => <div>Second Page</div>,
              2: () => <div>Third Page</div>
            }}
          </Switch>
        </div>
        <div className="flex justify-center gap-6">
          <MotionButton$
            className="bg-gray-300 rounded-lg px-6 py-2"
            animate={() => ({ opacity: page.get() === 0 ? 0.5 : 1 })}
            $disabled={() => page.get() === 0}
            onClick={() => page.set(p => p - 1)}
            transition={{ duration: 0.15 }}
          >
            Prev
          </MotionButton$>
          <MotionButton$
            className="bg-gray-300 rounded-lg px-6 py-2"
            animate={() => ({ opacity: page.get() === 2 ? 0.5 : 1 })}
            $disabled={() => page.get() === 2}
            onClick={() => page.set(p => p + 1)}
            transition={{ duration: 0.15 }}
          >
            Next
          </MotionButton$>
        </div>
      </motion.div>
    </motion.div>
  )
}
`}} />

## Router

<Example name="Router">
```jsx
function RouterExample() {
  const renderCount = ++useRef(0).current;

  return (
    <div style={{ width: 300 }}>
      <div>Renders: {renderCount}</div>
      <div>
        <button
          onClick={() => pageHashParams.page.delete()}
        >
          Go to root
        </button>
        <button
          onClick={() => pageHashParams.page.set('')}
        >
          Go to Page
        </button>
        <button
          onClick={() => pageHashParams.page.set('Home')}
        >
          Go Home
        </button>
        <button
          onClick={() => pageHashParams.page.set('asdf')}
        >
          Go to unknown
        </button>
      </div>
      <Switch value={pageHashParams.page}>
        {{
          undefined: () => <div>Root</div>,
          '': () => <div>Page</div>,
          Home: () => <div>Home</div>,
          default: () => <div>Unknown page</div>,
        }}
      </Switch>
    </div>
  );
}
```
</Example>
