# Getting Started

Getting Started — Legend State documentation.

Source: https://legend.so/state/v3/intro/getting-started/

Legend State v3 documentation. Current documentation version.

This guide will help you get started with Legend-State in a React or React Native App.

## [Which Platform?](https://legend.so/state/v3/intro/getting-started/#which-platform)

Select React or React Native to customize this guide for your platform.

### [Install Legend-State](https://legend.so/state/v3/intro/getting-started/#install-legend-state)

```bash
npm install @legendapp/state@beta
```

## [Quick Start](https://legend.so/state/v3/intro/getting-started/#quick-start)

We'll build a little Todo example app to show what a Legend-State app looks like. It works a bit differently than normal React apps - components re-render themselves when the state they care about changes. And Legend-State includes many helpful components to reduce the amount of boilerplate code you have to write, like components that two-way bind directly to state.

### [Create our first observable](https://legend.so/state/v3/intro/getting-started/#create-our-first-observable)

First we'll create an observable store for the example. An observable can be a single primitive or a massive tree of all of your state - it's up to you. It can infer its type from the data you initialize it with, or you can type it with an interface if you prefer, which we do in this example.

We'll set up the example with an array of todos, some computed functions to track counts, and an action function to add a todo. These functions can be within an observable or separate, it doesn't matter, but we will include it all together in this example.

```ts
import { observable } from "@legendapp/state";

// Type your Store interface
interface Todo {
  id: number;
  text: string;
  completed?: boolean;
}

interface Store {
  todos: Todo[];
  total: number;
  numCompleted: number;
  addTodo: () => void;
}

// Create a global observable for the Todos
let nextId = 0;
const store$ = observable<Store>({
  todos: [],
  // Computeds
  total: (): number => {
    return store$.todos.length;
  },
  numCompleted: (): number => {
    return store$.todos.get().filter((todo) => todo.completed).length;
  },
  addTodo: () => {
    const todo: Todo = {
      id: nextId++,
      text: "",
    };
    store$.todos.push(todo);
  },
});
```

Now that we have an observable for our Todos, let's hook it up to React.

### [Observables in React](https://legend.so/state/v3/intro/getting-started/#observables-in-react)

To consume an observable in React, just `useValue` it. This will track it automatically so that the component re-renders whenever it changes.

Legend-State also includes reactive components for both React and React Native. See [Reactive components](https://legend.so/state/v3/react/fine-grained-reactivity#reactive-components) for more about that.

```tsx
import { Button, Text, View } from "react-native";
import type { Observable } from "@legendapp/state"
import { For, observer, useValue, useObservable } from "@legendapp/state/react"
import { $TextInput } from "@legendapp/state/react-native"

export function App() {
    // Consume the computed observables from the global store$
    const total = useValue(store$.total)
    const completed = useValue(store$.numCompleted)
    // Create a local observable
    const theme$ = useObservable<'light' | 'dark'>('dark')
    const theme = useValue(theme$)

    const onClickClear = () => store$.todos.set([])

    return (
        <Box theme={theme}>
            <ThemeButton $value={theme$} />
            <Text>Total: {total}</Text>
            <Text>Completed: {completed}</Text>
            <For each={store$.todos} item={TodoItem} />
            <View className="flex justify-between">
                <Button title="Add" onPress={store$.addTodo} />
                <Button title="Clear" onPress={onClickClear} />
            </View>
        </Box>
    )
}

// Receives item$ prop from the For component
function TodoItem({ item$ }: { item$: Observable<Todo> }) {
    // The child components are bound directly to the observable properties
    // so this component never has to re-render.
    return (
        <View className="row">
            <Checkbox $value={item$.completed} />
            <$TextInput
                $value={item$.text}
                onSubmitEditing={() => store$.addTodo()}
            />
        </View>
    );
}
```

Now that our Todo app is rendering nicely, let's persist its state to storage.

### [Persistence](https://legend.so/state/v3/intro/getting-started/#persistence)

Legend-State has a built-in full-featured sync and persistence layer. In this example we'll show basic persistence and you can read [persist and sync](https://legend.so/state/v3/sync/persist-sync) for details.

In this example we set persistence options on the `store$` observable we created above. You can also configure a default persistence plugin globally.

Then all you have to do is `syncObservable` with the name you want it to have in storage. Any changes made after that will be saved to storage automatically.

```ts
import { syncObservable } from '@legendapp/state/sync'
import { ObservablePersistMMKV } from "@legendapp/state/persist-plugins/mmkv"

// Persist the observable to the named key of the persist plugin
syncObservable(store$, {
    persist: {
        name: 'gettingStarted',
        plugin: ObservablePersistMMKV
    }
})
```

And that's it! Now we have a full React app that persists its changes.

### [Full Example](https://legend.so/state/v3/intro/getting-started/#full-example)

Now let's put it all together into a live editable example. Feel free to play around in this sandbox on the left and see it running to the right.

```tsx
import { observable } from "@legendapp/state"
import { observer, useValue, useObservable } from "@legendapp/state/react"

interface Todo {
  id: number;
  text: string;
  completed?: boolean;
}

interface Store {
  todos: Todo[];
  total: number;
  numCompleted: number;
  addTodo: () => void;
}

// Create a global observable for the Todos
let nextId = 0;
const store$ = observable<Store>({
todos: [],
// Computeds
total: (): number => {
  return store$.todos.length;
},
numCompleted: (): number => {
  return store$.todos.get().filter((todo) => todo.completed).length;
},
addTodo: () => {
  const todo: Todo = {
    id: nextId++,
    text: "New todo",
  };
  store$.todos.push(todo);
},
});

// Receives item$ prop from the For component
function TodoItem({ item$ }) {
  const onKeyDown = (e) => {
      // Call addTodo from the global store$
      if (e.key === 'Enter') store$.addTodo()
  }

  return (
      <div className="flex items-center">
          <Checkbox $value={item$.completed} />
          <input
              value={useValue(item$.text)}
              onChange={(e) => item$.text.set(e.target.value)}
              onKeyDown={onKeyDown}
              style={{
                color: 'inherit',
                backgroundColor: 'rgba(255,255,255,0.1)',
                border: '1px solid #6b7280',
                borderRadius: '4px',
                padding: '4px 8px',
                marginLeft: '8px',
                width: '120px'
              }}
          />
      </div>
  );
}

function App() {
  const theme$ = useObservable('dark')
  const theme = useValue(theme$)
  const total = useValue(store$.total)
  const completed = useValue(store$.numCompleted)

  const onClickClear = () => store$.todos.set([])

  return (
      <Box theme={theme}>
          <ThemeButton $value={theme$} />
          <div>Total: {total}</div>
          <div>Completed: {completed}</div>
          <For each={store$.todos} item={TodoItem} />
          <div className="flex justify-between">
              <Button onClick={store$.addTodo}>Add</Button>
              <Button onClick={onClickClear}>Clear</Button>
          </div>
      </Box>
  )
}
```

[View Markdown](https://legend.so/state/v3/intro/getting-started.md)[Report a documentation correction](https://github.com/LegendApp/legend-docs/issues/new?title=Docs%3A+Getting+Started&body=Page%3A+https%3A%2F%2Flegend.so%2Fstate%2Fv3%2Fintro%2Fgetting-started%0A%0AWhat+is+incorrect+or+missing%3F%0A%0AExpected+behavior+or+supporting+source%3A%0A)
