Build a Todo App with Laravel, React & Inertia.js: Zero API Headaches!

Author

Kritim Yantra

Jun 10, 2025

Build a Todo App with Laravel, React & Inertia.js: Zero API Headaches!

πŸ’‘ Why This Combo is Magic

Imagine building a LEGO castle:

  • Laravel = Foundation plates (backend structure)
  • React = Colorful bricks (UI components)
  • Inertia.js = Instruction manual (seamless connection)

No glue, no duct tape – everything snaps together perfectly! Today, we’ll build a fully functional Todo app without writing a single API endpoint. Let’s dive in!


πŸ› οΈ Setup: Install the Power Trio

1. Create Laravel Project

laravel new inertia-todo  
cd inertia-todo  

2. Install Inertia + React

composer require inertiajs/inertia-laravel  
npm install @inertiajs/react react react-dom  

3. Configure Inertia

Update resources/views/app.blade.php:

<!DOCTYPE html>  
<html>  
  <head>  
    @vite('resources/js/app.js')  
  </head>  
  <body>  
    <div id="app" data-page="{{ json_encode($page) }}"></div>  
  </body>  
</html>  

Create resources/js/app.js:

import { createInertiaApp } from '@inertiajs/react';  
import { createRoot } from 'react-dom/client';  

createInertiaApp({  
  resolve: name => require(`./Pages/${name}`),  
  setup({ el, App, props }) {  
    createRoot(el).render(<App {...props} />);  
  },  
});  

πŸ“¦ Step 1: Create Todo Model & Migration

Generate Model + Migration

php artisan make:model Todo -m  

Edit migration file (database/migrations/..._create_todos_table.php):

public function up() {  
  Schema::create('todos', function (Blueprint $table) {  
    $table->id();  
    $table->string('title');  
    $table->boolean('completed')->default(false);  
    $table->timestamps();  
  });  
}  

Run migration:

php artisan migrate  

πŸ”„ Step 2: Build Laravel Controller

Create Controller

php artisan make:controller TodoController  

Edit app/Http/Controllers/TodoController.php:

use App\Models\Todo;  
use Inertia\Inertia;  
use Illuminate\Http\Request;  

class TodoController extends Controller {  
  // Show all todos  
  public function index() {  
    return Inertia::render('Todos/Index', [  
      'todos' => Todo::latest()->get()  
    ]);  
  }  

  // Add new todo  
  public function store(Request $request) {  
    Todo::create($request->validate(['title' => 'required|min:3']));  
    return redirect()->back();  
  }  

  // Update completion status  
  public function update(Request $request, Todo $todo) {  
    $todo->update(['completed' => $request->completed]);  
    return redirect()->back();  
  }  

  // Delete todo  
  public function destroy(Todo $todo) {  
    $todo->delete();  
    return redirect()->back();  
  }  
}  

πŸ“ Step 3: Define Routes

Edit routes/web.php:

use App\Http\Controllers\TodoController;  

Route::get('/', [TodoController::class, 'index']);  
Route::post('/todos', [TodoController::class, 'store']);  
Route::put('/todos/{todo}', [TodoController::class, 'update']);  
Route::delete('/todos/{todo}', [TodoController::class, 'destroy']);  

βš›οΈ Step 4: Build React Components

1. Main Page (resources/js/Pages/Todos/Index.jsx)

import { Head, Link } from '@inertiajs/react';  
import TodoForm from './Form';  
import TodoList from './List';  

export default function TodoIndex({ todos }) {  
  return (  
    <div className="max-w-2xl mx-auto p-8">  
      <Head title="Your Todo List" />  
      <h1 className="text-3xl font-bold mb-6">✨ Your Todo List</h1>  
      <TodoForm />  
      <TodoList todos={todos} />  
    </div>  
  );  
}  

2. Todo Form (resources/js/Pages/Todos/Form.jsx)

import { useForm } from '@inertiajs/react';  

export default function TodoForm() {  
  const { data, setData, post, processing } = useForm({  
    title: ''  
  });  

  const submit = (e) => {  
    e.preventDefault();  
    post('/todos');  
  };  

  return (  
    <form onSubmit={submit} className="mb-8">  
      <input  
        type="text"  
        value={data.title}  
        onChange={e => setData('title', e.target.value)}  
        placeholder="Buy groceries..."  
        className="w-full p-3 border rounded"  
        disabled={processing}  
      />  
      <button  
        type="submit"  
        className="mt-2 bg-blue-500 text-white p-2 rounded"  
        disabled={processing}  
      >  
        {processing ? 'Adding...' : 'Add Task'}  
      </button>  
    </form>  
  );  
}  

3. Todo List (resources/js/Pages/Todos/List.jsx)

import { Link } from '@inertiajs/react';  

export default function TodoList({ todos }) {  
  return (  
    <div className="space-y-4">  
      {todos.map(todo => (  
        <div key={todo.id} className="flex items-center justify-between p-4 bg-white shadow rounded">  
          <div className="flex items-center">  
            <input  
              type="checkbox"  
              checked={todo.completed}  
              onChange={() => Inertia.put(`/todos/${todo.id}`, {  
                completed: !todo.completed  
              })}  
              className="mr-3 h-5 w-5"  
            />  
            <span className={todo.completed ? 'line-through text-gray-500' : ''}>  
              {todo.title}  
            </span>  
          </div>  
          <button  
            onClick={() => Inertia.delete(`/todos/${todo.id}`)}  
            className="text-red-500 hover:text-red-700"  
          >  
            Delete  
          </button>  
        </div>  
      ))}  
    </div>  
  );  
}  

πŸš€ Launch Your App!

  1. Start backend:
    php artisan serve  
    
  2. Start frontend:
    npm run dev  
    
  3. Visit http://localhost:8000

Behold! Your fully functional Todo app:

  • βœ… Add tasks
  • βœ… Mark as complete
  • βœ… Delete items
  • ⚑ All updates happen without page reloads!

πŸ” How It Works: The Inertia Magic

  1. User adds task:

    • React form β†’ Laravel store() method β†’ Database
    • No API layer!
  2. User checks todo:

    • Inertia sends PUT request β†’ Laravel update() method
    • Database updated β†’ React UI instantly re-renders
  3. User deletes item:

    • Inertia.delete() β†’ Laravel destroy()
    • Item vanishes from UI instantly

✨ Key Takeaways

  1. No API Boilerplate: Inertia lets Laravel controllers talk directly to React!
  2. Real-Time UI Updates: Pages update without reloads (SPA experience).
  3. Shared Validation: Use Laravel validation rules in React forms.
  4. Blazing Fast Dev: Build full-stack features in record time.

πŸ’‘ Pro Tip: Add Laravel Breeze for authentication:

composer require laravel/breeze --dev  
php artisan breeze:install react  

πŸš€ Your Next Challenge

Enhance your Todo app with:

  • Due dates
  • Priority levels
  • Drag-and-drop sorting

Questions? Drop them below! πŸ‘‡

LIVE MENTORSHIP ONLY 5 SPOTS

Laravel Mastery
Coaching Class Program

KritiMyantra

Transform from beginner to Laravel expert with our personalized Coaching Class starting June 20, 2025. Limited enrollment ensures focused attention.

Daily Sessions

1-hour personalized coaching

Real Projects

Build portfolio applications

Best Practices

Industry-standard techniques

Career Support

Interview prep & job guidance

Total Investment
$200
Duration
30 hours
1h/day

Enrollment Closes In

Days
Hours
Minutes
Seconds
Spots Available 5 of 10 remaining
Next cohort starts:
June 20, 2025

Join the Program

Complete your application to secure your spot

Application Submitted!

Thank you for your interest in our Laravel mentorship program. We'll contact you within 24 hours with next steps.

What happens next?

  • Confirmation email with program details
  • WhatsApp message from our team
  • Onboarding call to discuss your goals

Tags

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Sign in with Google

Related Posts