Documentation Index
Fetch the complete documentation index at: https://docs.basic.tech/llms.txt
Use this file to discover all available pages before exploring further.
In this guide, you will:
- Set up a new React project with Basic
- Create a simple To-Do UI
- Connect to a Basic project and set up your schema
- Add sign in and sign out functionality
- Hook up to Basic DB so that your users can save their to-dos across their devices
Create a new React project
Create a new Vite React project
npm create vite@latest my-todo-app -- --template react-ts
cd my-todo-app
npm install
Install Basic and Tailwind
npm install @basictech/react
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Configure Tailwind
Update tailwind.config.js:/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Replace src/index.css with:@tailwind base;
@tailwind components;
@tailwind utilities;
Start your dev server
Navigate to http://localhost:5173 to see your app running.
Create a simple To-Do UI
Create the basic layout
Replace the code in your src/App.tsx file:import './App.css'
function App() {
return (
<div className="flex flex-col items-center p-8">
<h1 className="text-2xl font-bold mb-4">my lofi to-do app</h1>
<div className="flex gap-2">
<input
type="text"
placeholder="Add a to-do"
className="border rounded px-2 py-1"
/>
<button className="bg-blue-500 text-white px-3 py-1 rounded">
Add
</button>
</div>
</div>
)
}
export default App
Connect to a Basic project
Create a Basic account
Go to admin.basic.tech and create a free account. Then create a new project and copy your Project ID. Create basic.config.ts
Create a basic.config.ts file in your project root:export const schema = {
project_id: "YOUR_PROJECT_ID", // Replace with your project ID
version: 0,
tables: {
todos: {
type: "collection",
fields: {
name: {
type: "string"
},
completed: {
type: "boolean"
}
}
}
}
}
Add BasicProvider to main.tsx
Update src/main.tsx:import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BasicProvider } from '@basictech/react'
import { schema } from '../basic.config'
import App from './App'
import './index.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BasicProvider schema={schema}>
<App />
</BasicProvider>
</StrictMode>,
)
Add sign in and sign out functionality
Add auth buttons to App.tsx
Update src/App.tsx:import { useBasic } from '@basictech/react'
import './App.css'
function App() {
const { signIn, signOut, isSignedIn, user } = useBasic()
return (
<div className="flex flex-col items-center p-8">
<h1 className="text-2xl font-bold mb-4">my lofi to-do app</h1>
<div className="flex gap-2">
<input
type="text"
placeholder="Add a to-do"
className="border rounded px-2 py-1"
/>
<button className="bg-blue-500 text-white px-3 py-1 rounded">
Add
</button>
</div>
<div className="mt-8">
{isSignedIn ? (
<div className="text-center">
<p>Signed in as: {user?.email}</p>
<button
onClick={signOut}
className="mt-2 text-red-500 hover:underline"
>
Sign Out
</button>
</div>
) : (
<button
onClick={signIn}
className="bg-green-500 text-white px-4 py-2 rounded"
>
Sign In
</button>
)}
</div>
</div>
)
}
export default App
Hook up to Basic DB
Add task creation
Update src/App.tsx to add state and the addTask function:import { useBasic } from '@basictech/react'
import { useState } from 'react'
import './App.css'
function App() {
const { signIn, signOut, isSignedIn, user, db } = useBasic()
const [taskInput, setTaskInput] = useState('')
const addTask = async () => {
if (!taskInput.trim()) return
await db.collection('todos').add({
name: taskInput,
completed: false
})
setTaskInput('')
}
return (
<div className="flex flex-col items-center p-8">
<h1 className="text-2xl font-bold mb-4">my lofi to-do app</h1>
<div className="flex gap-2">
<input
type="text"
placeholder="Add a to-do"
className="border rounded px-2 py-1"
value={taskInput}
onChange={(e) => setTaskInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && addTask()}
/>
<button
className="bg-blue-500 text-white px-3 py-1 rounded"
onClick={addTask}
>
Add
</button>
</div>
<div className="mt-8">
{isSignedIn ? (
<div className="text-center">
<p>Signed in as: {user?.email}</p>
<button
onClick={signOut}
className="mt-2 text-red-500 hover:underline"
>
Sign Out
</button>
</div>
) : (
<button
onClick={signIn}
className="bg-green-500 text-white px-4 py-2 rounded"
>
Sign In
</button>
)}
</div>
</div>
)
}
export default App
Display and delete tasks
Add useQuery to display tasks and a delete function:import { useBasic, useQuery } from '@basictech/react'
import { useState } from 'react'
import './App.css'
function App() {
const { signIn, signOut, isSignedIn, user, db } = useBasic()
const [taskInput, setTaskInput] = useState('')
// Subscribe to all todos - auto-updates when data changes
const tasks = useQuery(() => db.collection('todos').getAll())
const addTask = async () => {
if (!taskInput.trim()) return
await db.collection('todos').add({
name: taskInput,
completed: false
})
setTaskInput('')
}
const deleteTask = async (id: string) => {
await db.collection('todos').delete(id)
}
return (
<div className="flex flex-col items-center p-8">
<h1 className="text-2xl font-bold mb-4">my lofi to-do app</h1>
<div className="flex gap-2">
<input
type="text"
placeholder="Add a to-do"
className="border rounded px-2 py-1"
value={taskInput}
onChange={(e) => setTaskInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && addTask()}
/>
<button
className="bg-blue-500 text-white px-3 py-1 rounded"
onClick={addTask}
>
Add
</button>
</div>
{/* Task list */}
<div className="mt-6 w-full max-w-md">
{tasks?.map((task: any) => (
<div
key={task.id}
className="flex items-center justify-between p-3 border-b"
>
<span>{task.name}</span>
<button
onClick={() => deleteTask(task.id)}
className="text-red-500 hover:text-red-700"
>
Delete
</button>
</div>
))}
</div>
<div className="mt-8">
{isSignedIn ? (
<div className="text-center">
<p>Signed in as: {user?.email}</p>
<button
onClick={signOut}
className="mt-2 text-red-500 hover:underline"
>
Sign Out
</button>
</div>
) : (
<button
onClick={signIn}
className="bg-green-500 text-white px-4 py-2 rounded"
>
Sign In
</button>
)}
</div>
</div>
)
}
export default App
And that’s it! You’ve built a local-first to-do app that allows your users to sign in and save their to-dos across their devices.
Notice how snappily all the changes are reflected - from task creation to deletion. This is the power of local-first architecture!
Next steps