1. Set up starter project with React, Vite & Tailwind
Set up a starter project with React, Vite & Tailwind
Section titled “Set up a starter project with React, Vite & Tailwind”We have prepared a starter project for you that you can use as a starting point for the tutorial. Download it via the following command:
bunx @livestore/cli@dev create \ --example tutorial-starter livestore-todo-apppnpm dlx @livestore/cli@dev create \ --example tutorial-starter livestore-todo-appOnce you’ve downloaded the project, you can navigate to the project directory and install the dependencies:
The project currently is set up as follows:
- Minimal project created via
vite createusing React and TypeScript. - Using Tailwind CSS for styling.
- Has basic functionality for adding and deleting todos via local
React.useState().
Understand the current project state
Section titled “Understand the current project state”Run the app with:
bun devpnpm devHere’s the UI you’re going to see after adding a few todos:

Let’s take a quick moment to understand how the app is currently implemented:
All relevant code lives in App.tsx. Here’s a simplified version of it:
interface Todo { id: number text: string}
function App() { const [todos, setTodos] = useState<Todo[]>([]) const [input, setInput] = useState('')
const addTodo = () => { const newTodo: Todo = { id: Date.now(), text: input } setTodos([...todos, newTodo]) setInput('') }
const deleteTodo = (id: number) => { setTodos(todos.filter(todo => todo.id !== id)) }
return ( // Render input text field and todo list ... // ... and invoke `addTodo` and `deleteTodo` // ... when the buttons are clicked. )}For any React developer, this is a very familiar setup:
You have two pieces of state:
- application state:
todos: Todo[]→ manipulated by theaddTodoanddeleteTodofunctions. - UI state:
input: string→ manipulated when the text in the input field changes.
The “problem” with this code is that the todo items are not persisted, meaning they vanish when:
- the page is refreshed in the browser.
- the development server is restarted.
In the next chapters, you’ll learn how to persist the todos in the list, so that they’ll “survive” both actions.
Even more: They will not only persist, they will automatically sync across multiple browsers tabs/windows, and even across devices—without you needing to think about the syncing logic and managing remote state.
That’s the power of LiveStore!