Quick Start Guide
Get up and running with light-hooks in 5 minutes. Transform your React components with powerful, lightweight hooks.
Ready to supercharge your React app? This guide will get you from zero to hero with light-hooks in just a few minutes!
Note:
This guide assumes you have a React project already set up. If you don't, create one with npx create-react-app my-app
or npm create vite@latest my-app -- --template react
.
Build a Complete Todo App
We'll build a fully functional todo app showcasing the most popular light-hooks. By the end, you'll have a real app and understand how to use:
useLocalStorage
- for persistent datauseToggle
- for boolean statesuseCounter
- for managing numbersuseDebounce
- for search optimization
Step 1: Install and Setup
Install light-hooks in your project:
npm install light-hooks
Create a new component file TodoApp.jsx
(or .tsx
for TypeScript):
import React, { useState } from 'react';
import {
useLocalStorage,
useToggle,
useCounter,
useDebounce
} from 'light-hooks';
export default function TodoApp() {
return <div>Let's build something awesome!</div>;
}
Step 2: Persistent Todo Storage
Replace the basic useState with persistent storage:
function TodoApp() {
// This replaces useState and automatically syncs with localStorage!
const [todos, setTodos] = useLocalStorage('my-todos', []);
const [newTodo, setNewTodo] = useState('');
const addTodo = () => {
if (newTodo.trim()) {
setTodos([...todos, {
id: Date.now(),
text: newTodo,
completed: false
}]);
setNewTodo('');
}
};
return (
<div style={{ padding: '20px', maxWidth: '500px', margin: '0 auto' }}>
<h1>My Awesome Todo App</h1>
<div style={{ marginBottom: '20px' }}>
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="What needs to be done?"
style={{ padding: '10px', marginRight: '10px', width: '300px' }}
/>
<button onClick={addTodo} style={{ padding: '10px' }}>
Add Todo
</button>
</div>
<div>
{todos.map(todo => (
<div key={todo.id} style={{ padding: '10px', border: '1px solid #ddd', margin: '5px 0' }}>
{todo.text}
</div>
))}
</div>
</div>
);
}
Note:
Amazing! Your todos now persist automatically. Refresh the page - they're still there!
Step 3: Add Toggle Functionality
Let's add the ability to mark todos as complete using useToggle
:
function TodoItem({ todo, onToggle, onDelete }) {
return (
<div style={{
padding: '10px',
border: '1px solid #ddd',
margin: '5px 0',
display: 'flex',
justifyContent: 'space-between',
backgroundColor: todo.completed ? '#f0f8f0' : 'white'
}}>
<span
style={{
textDecoration: todo.completed ? 'line-through' : 'none',
color: todo.completed ? '#888' : 'black'
}}
>
{todo.text}
</span>
<div>
<button
onClick={() => onToggle(todo.id)}
style={{ marginRight: '10px', padding: '5px 10px' }}
>
{todo.completed ? 'Undo' : 'Done'}
</button>
<button
onClick={() => onDelete(todo.id)}
style={{ padding: '5px 10px', backgroundColor: '#ff4444', color: 'white' }}
>
Delete
</button>
</div>
</div>
);
}
function TodoApp() {
const [todos, setTodos] = useLocalStorage('my-todos', []);
const [newTodo, setNewTodo] = useState('');
// Perfect for show/hide states!
const [showCompleted, toggleShowCompleted] = useToggle(true);
const addTodo = () => {
if (newTodo.trim()) {
setTodos([...todos, {
id: Date.now(),
text: newTodo,
completed: false
}]);
setNewTodo('');
}
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const filteredTodos = showCompleted
? todos
: todos.filter(todo => !todo.completed);
return (
<div style={{ padding: '20px', maxWidth: '500px', margin: '0 auto' }}>
<h1>My Awesome Todo App</h1>
<div style={{ marginBottom: '20px' }}>
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="What needs to be done?"
style={{ padding: '10px', marginRight: '10px', width: '300px' }}
/>
<button onClick={addTodo} style={{ padding: '10px' }}>
Add Todo
</button>
</div>
<div style={{ marginBottom: '20px' }}>
<button
onClick={toggleShowCompleted}
style={{ padding: '10px' }}
>
{showCompleted ? 'Hide' : 'Show'} Completed
</button>
</div>
<div>
{filteredTodos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
</div>
</div>
);
}
Step 4: Add Statistics with useCounter
Let's add some stats using useCounter
:
function TodoStats({ todos }) {
// useCounter provides increment, decrement, reset, and set methods!
const [viewCount, { increment: incrementViews }] = useCounter(0);
React.useEffect(() => {
incrementViews(); // Count each time stats are viewed
}, []);
const completed = todos.filter(todo => todo.completed).length;
const total = todos.length;
const pending = total - completed;
return (
<div style={{
padding: '15px',
backgroundColor: '#f5f5f5',
borderRadius: '8px',
margin: '20px 0'
}}>
<h3>Statistics</h3>
<p>Total: {total}</p>
<p>Completed: {completed}</p>
<p>Pending: {pending}</p>
<p>Times viewed: {viewCount}</p>
</div>
);
}
// Add <TodoStats todos={todos} /> to your TodoApp component
Step 5: Add Smart Search with useDebounce
Finally, let's add a search feature that doesn't spam the filter function:
function TodoApp() {
const [todos, setTodos] = useLocalStorage('my-todos', []);
const [newTodo, setNewTodo] = useState('');
const [showCompleted, toggleShowCompleted] = useToggle(true);
// Search functionality
const [searchTerm, setSearchTerm] = useState('');
// Only searches after user stops typing for 300ms!
const debouncedSearch = useDebounce(searchTerm, 300);
// ... other functions ...
const filteredTodos = todos
.filter(todo => showCompleted || !todo.completed)
.filter(todo =>
debouncedSearch
? todo.text.toLowerCase().includes(debouncedSearch.toLowerCase())
: true
);
return (
<div style={{ padding: '20px', maxWidth: '500px', margin: '0 auto' }}>
<h1>My Awesome Todo App</h1>
{/* Add Todo Section */}
<div style={{ marginBottom: '20px' }}>
<input
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="What needs to be done?"
style={{ padding: '10px', marginRight: '10px', width: '300px' }}
/>
<button onClick={addTodo} style={{ padding: '10px' }}>
Add Todo
</button>
</div>
{/* Search Section */}
<div style={{ marginBottom: '20px' }}>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search todos..."
style={{ padding: '10px', width: '100%' }}
/>
</div>
{/* Controls */}
<div style={{ marginBottom: '20px' }}>
<button
onClick={toggleShowCompleted}
style={{ padding: '10px' }}
>
{showCompleted ? 'Hide' : 'Show'} Completed
</button>
</div>
{/* Stats */}
<TodoStats todos={todos} />
{/* Todo List */}
<div>
{filteredTodos.length === 0 ? (
<p style={{ textAlign: 'center', color: '#888' }}>
{debouncedSearch ? 'No todos match your search' : 'No todos yet. Add one above!'}
</p>
) : (
filteredTodos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))
)}
</div>
</div>
);
}
Congratulations!
You've just built a complete todo app using light-hooks! Here's what you accomplished:
- Persistent Storage: Todos survive page refreshes with
useLocalStorage
- Smart Toggles: Show/hide completed items with
useToggle
- Statistics: View counting with
useCounter
- Optimized Search: Debounced filtering with
useDebounce
Note:
Pro Tip: Your app automatically handles edge cases like empty states, persistence, and performance optimization - all thanks to light-hooks!
What's Next?
Now that you've mastered the basics, explore more hooks:
useInterval
- for auto-refresh functionalityuseTimeout
- for delayed actionsusePrevious
- to compare with previous valuesuseUpdateEffect
- useEffect that skips first render
Ready to dive deeper? Check out our individual hook documentation for advanced patterns and real-world examples!
Key Takeaways
- Less Boilerplate: light-hooks eliminate common React patterns
- Built-in Persistence:
useLocalStorage
handles serialization automatically - Performance Optimized:
useDebounce
prevents excessive function calls - TypeScript Ready: Full type safety out of the box
- Tree Shakable: Import only what you need