En este tutorial aprenderás a crear un CRUD en Laravel Inertia de forma completa haciendo uso de las herramientas más típicas en el desarrollo de aplicaciones web con Laravel.
Si quieres aprender a crear un CRUD en Laravel y Blade, no te pierdas esta entrada, tienes un paso a paso completo desde cero incluido el proyecto final para que lo puedas descargar.
Si quieres aprender a crear un CRUD en Laravel Livewire, no te pierdas esta entrada, tienes un paso a paso completo desde cero incluido el proyecto final para que lo puedas descargar.
Para llevar a cabo nuestro CRUD en Laravel Inertia, vamos a utilizar Vue 3. Si no tienes conocimientos sólidos de este framework, antes deberás llevar a cabo este curso gratuito.

Proyecto final
Descarga el proyecto final desde GitHub.
Crear proyecto Laravel Inertia
Lo primero que debemos hacer para desarrollar un CRUD en Laravel Inertia es crear nuestro proyecto, en nuestro caso lo haremos con el instalador de Laravel haciendo uso de Jetstream:
laravel new crud-laravel-inertia --jet
cd crud-laravel-inertia
npm run devEn el proceso de creación del proyecto debes seleccionar la opción [1] Inertia:
Which Jetstream stack do you prefer?
[0] livewire
[1] inertia
>Con los comandos anteriores ya tendremos nuestro proyecto creado haciendo uso de Jetstream con Inertia.js.
Modelo y migración
Ahora vamos a crear un modelo, una migración y un controlador para gestionar los proyectos que utilizaremos para llevar a cabo nuestro CRUD:
php artisan make:model Project -mcEl comando anterior habrá creado el modelo, una migración y un controlador para gestionar los proyectos en nuestra aplicación CRUD, vamos a hacer algunos ajustes:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\User;
return new class extends Migration
{
public function up()
{
Schema::create('projects', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class)->constrained();
$table->string('name', 100)->unique();
$table->text('description');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('projects');
}
};Ahora vamos a abrir nuestro modelo Project para hacer algunos ajustes más:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Project extends Model
{
protected $fillable = [
'name',
'description',
];
protected static function boot()
{
parent::boot();
static::creating(function ($project) {
$project->user_id = auth()->id();
});
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}Ahora generemos nuestra base de datos con la tabla de proyectos, pero recuerda primero actualizar las credenciales de tu base de datos en el archivo .env:
php artisan migrate:freshRutas CRUD en Laravel Inertia
Vamos a abrir el archivo de rutas web.php para añadir la siguiente línea, la cual nos ayudará a crear todos los endpoints para nuestro CRUD:
<?php
Route::middleware([
'auth:sanctum',
config('jetstream.auth_session'),
'verified',
])->group(function () {
// ...
Route::resource('projects', ProjectController::class);
});El método resource generará los siguientes endpoints en nuestra aplicación:
GET|HEAD projects .............................................. projects.index › ProjectController@index
POST projects .............................................. projects.store › ProjectController@store
GET|HEAD projects/create ....................................... projects.create › ProjectController@create
GET|HEAD projects/{project} .................................... projects.show › ProjectController@show
PUT|PATCH projects/{project} .................................... projects.update › ProjectController@update
DELETE projects/{project} .................................... projects.destroy › ProjectController@destroy
GET|HEAD projects/{project}/edit ............................... projects.edit › ProjectController@editActualizar la navegación de Vue
Cuando trabajamos con Inertia, por defecto el framework es Vue, y cualquier cosa que deseemos modificar en la interfaz se encuentra en el directorio resources/js. En nuestro caso, lo que nos interesa modificar en la navegación que se encuentra en el layout de usuarios identificados. Podemos modificar esto abriendo el archivo resources/js/Layouts/AppLayout.vue:
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<NavLink :href="route('dashboard')" :active="route().current('dashboard')">
Dashboard
</NavLink>
<!-- Añadimos la navegación a proyectos -->
<NavLink :href="route('projects.index')" :active="route().current('projects.index')">
Proyectos
</NavLink>
</div>
CRUD Laravel Inertia: Listar proyectos con paginación
Vamos a añadir el método index a nuestro controlador ProjectController para obtener los proyectos paginados y así poder mostrarlos en un componente Vue más adelante:
<?php
namespace App\Http\Controllers;
use App\Models\Project;
use Inertia\Response;
class ProjectController extends Controller
{
public function index(): Response
{
$projects = Project::with('user:id,name')->paginate(2);
return inertia('Projects/Index', [
'projects' => $projects
]);
}
}Nuestro método index simplemente obtiene los proyectos paginados utilizando Eloquent y retorna un componente Vue que contendrá esa información para poder mostrarla a través de sus props, creemos ese componente, recuerda crearlo en el directorio resources/js/Pages, aquí es donde van las vistas que renderizamos con Laravel:
<script>
export default {
name: 'ProjectsIndex',
}
</script>
<script setup>
import AppLayout from '@/Layouts/AppLayout.vue';
import { Inertia } from "@inertiajs/inertia";
import { Link } from '@inertiajs/inertia-vue3';
defineProps({
projects: {
type: Object,
required: true,
},
})
const deleteProject = id => {
if (confirm('¿Estás seguro de querer eliminar este proyecto?')) {
Inertia.delete(route('projects.destroy', id))
}
}
</script>
<template>
<AppLayout title="Proyectos">
<template #header>
<h1 class="font-semibold text-xl text-gray-800 leading-tight">
Proyectos
</h1>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<div class="flex justify-between">
<Link
:href="route('projects.create')"
class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded"
>
Crear proyecto
</Link>
</div>
<div class="mt-4">
<table class="table-auto w-full">
<thead class="text-xs font-semibold uppercase text-gray-400 bg-gray-50">
<tr>
<th class="px-4 py-2">Nombre</th>
<th class="px-4 py-2">Autor</th>
<th class="px-4 py-2">Descripción</th>
<th class="px-4 py-2">Acciones</th>
</tr>
</thead>
<tbody class="text-sm divide-y divide-gray-100" v-if="projects.total > 0">
<template v-for="project in projects.data">
<tr>
<td class="border px-4">{{ project.name }}</td>
<td class="border px-4">{{ project.user.name }}</td>
<td class="border px-4">{{ project.description }}</td>
<td class="border px-4 py-4" style="width: 300px">
<Link
:href="route('projects.edit', project.id)"
class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded"
>
Editar
</Link>
<Link
:href="route('projects.show', project.id)"
class="ml-2 bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded"
>
Ver
</Link>
<Link
class="ml-2 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
@click="deleteProject(project.id)"
>
Eliminar
</Link>
</td>
</tr>
</template>
</tbody>
<tbody v-else>
<tr class="bg-red-400 text-white text-center">
<td colspan="4" class="border px-4 py-2">No hay proyectos para mostrar</td>
</tr>
</tbody>
<tfoot
v-if="projects.last_page > 1"
class="text-xs font-semibold uppercase text-gray-400 bg-gray-50"
>
<tr>
<td colspan="4" class="border px-4 py-4 text-right">
<Link
v-if="projects.current_page > 1"
:href="projects.prev_page_url"
class="px-3 py-1 bg-indigo-500 hover:bg-gray-600 text-white rounded"
>
Anterior
</Link>
<Link
v-if="projects.current_page < projects.last_page"
:href="projects.next_page_url"
class="ml-2 px-3 py-1 bg-indigo-500 hover:bg-gray-600 text-white rounded"
>
Siguiente
</Link>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</AppLayout>
</template>
Si tienes dudas acerca de la sintaxis utilizada en el tag script setup, te recomiendo el curso de Vue 3 Script Setup, está todo explicado.
defineProps: Aquí recogemos los proyectos enviados desde el controlador.
deleteProject: Función para eliminar un proyecto con Inertia.
AppLayout: Layout que viene con Jetstream y el stack Inertia.
Link: Componente de Inertia que nos permite navegar por la página sin refrescar.
CRUD Laravel Inertia: Crear proyectos
Vamos a añadir el método create a nuestro controlador ProjectController para mostrar un componente formulario con Vue que nos permita crear nuevos proyectos:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ProjectRequest;
use App\Models\Project;
use Inertia\Response;
class ProjectController extends Controller
{
// ...
public function create(): Response
{
return inertia('Projects/Create');
}
}Ahora, vamos a crear nuevo archivo en resources/js/Pages/Projects llamado Create.vue, el cual más adelante utilizaré un componente Vue para representar el formulario de proyectos:
<script>
export default {
name: 'ProjectsCreate',
}
</script>
<script setup>
import { useForm } from "@inertiajs/inertia-vue3";
import AppLayout from '@/Layouts/AppLayout.vue';
import ProjectForm from '@/Components/Projects/Form.vue';
const form = useForm({
name: '',
description: '',
});
</script>
<template>
<AppLayout title="Crear proyecto">
<template #header>
<h1 class="font-semibold text-xl text-gray-800 leading-tight">
Crear proyecto
</h1>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<ProjectForm
:form="form"
@submit="form.post(route('projects.store'))"
/>
</div>
</div>
</div>
</div>
</AppLayout>
</template>useForm: Helper proporcionado por Inertia para facilitar el trabajo con formularios.
ProjectForm: El componente que generaremos a continuación para representar un formulario de proyectos, sea para alta o edición.
CRUD Laravel Inertia: Formulario proyectos alta/edición
Vamos a crear un nuevo componente en resources/js/Components/Projects llamado Form.vue, este archivo será el formulario que utilizaremos para crear y editar proyectos.
<script>
export default {
name: 'ProjectsForm'
}
</script>
<script setup>
import FormSection from '@/Components/FormSection.vue';
import InputError from '@/Components/InputError.vue';
import InputLabel from '@/Components/InputLabel.vue';
import PrimaryButton from '@/Components/PrimaryButton.vue';
import TextInput from '@/Components/TextInput.vue';
import TextAreaInput from '@/Components/TextAreaInput.vue';
defineProps({
form: {
type: Object,
required: true,
},
updating: {
type: Boolean,
required: false,
default: false,
},
})
defineEmits(['submit'])
</script>
<template>
<FormSection @submitted="$emit('submit')">
<template #title>
{{ updating ? 'Editar proyecto' : 'Crear nuevo proyecto' }}
</template>
<template #description>
{{ updating ? 'Actualiza tu proyecto' : 'Crea un nuevo proyecto.' }}
</template>
<template #form>
<div class="col-span-6 sm:col-span-6">
<InputLabel for="name" value="Name" />
<TextInput
id="name"
v-model="form.name"
type="text"
class="mt-1 block w-full"
autocomplete="name"
/>
<InputError :message="$page.props.errors.name" class="mt-2" />
</div>
<div class="col-span-6 sm:col-span-6">
<InputLabel for="description" value="Description" />
<TextAreaInput
id="description"
rows="5"
v-model="form.description"
type="text"
class="mt-1 block w-full"
autocomplete="description"
/>
<InputError :message="$page.props.errors.description" class="mt-2" />
</div>
</template>
<template #actions>
<PrimaryButton>
{{ updating ? 'Actualizar' : 'Crear' }}
</PrimaryButton>
</template>
</FormSection>
</template>$page.props.errors: Es un objeto que nos proporciona Inertia con los errores de formulario que puedan haber.
Si ahora visitamos la ruta para crear nuevos proyectos deberíamos ver lo siguiente:

CRUD Laravel Inertia: Validar y guardar proyectos en base de datos
Empecemos por crear un FormRequest que nos permita validar tanto el alta como la edición de proyectos:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class ProjectRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'name' => [
'required',
'string',
'max:100',
Rule::unique(table: 'projects', column: 'name')->ignore(id: request('project'), idColumn: 'id'),
],
'description' => 'required|string|max:1000',
];
}
public function messages(): array
{
return [
'name.required' => __('El campo nombre es obligatorio.'),
'name.string' => __('El campo nombre debe ser una cadena de texto.'),
'name.max' => __('El campo nombre no debe ser mayor a :max caracteres.'),
'name.unique' => __('El campo nombre ya está en uso.'),
'description.required' => __('El campo descripción es obligatorio.'),
'description.string' => __('El campo descripción debe ser una cadena de texto.'),
'description.max' => __('El campo descripción no debe ser mayor a :max caracteres.'),
];
}
}Ahora vamos a añadir el método store a nuestro controlador para validar y procesar nuestro formulario de alta de proyectos:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ProjectRequest;
use App\Models\Project;
use Inertia\Response;
class ProjectController extends Controller
{
// ...
public function store(ProjectRequest $request)
{
$project = Project::create($request->validated());
return redirect()->route('projects.show', $project);
}
}Con lo anterior, podrás dar de alta nuevos proyectos validando los datos, en caso de que exista algún error, éstos serán mostrados en el formulario de Vue.
CRUD Laravel Inertia: Detalle de un proyecto
Ahora vamos a añadir el método show a nuestro controlador para poder mostrar el detalle de un proyecto en un nuevo componente Vue:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ProjectRequest;
use App\Models\Project;
use Inertia\Response;
class ProjectController extends Controller
{
// ...
public function show(Project $project): Response
{
return inertia('Projects/Detail', [
'project' => $project->load('user:id,name')
]);
}
}Crea un archivo resources/js/Pages/Projects/Detail.vue para mostrar el detalle de un proyecto:
<script>
export default {
name: 'ProjectDetail',
}
</script>
<script setup>
import { Link } from "@inertiajs/inertia-vue3";
import { Inertia } from "@inertiajs/inertia";
import AppLayout from '@/Layouts/AppLayout.vue';
defineProps({
project: {
type: Object,
required: true,
},
})
const deleteProject = id => {
if (confirm('¿Estás seguro de querer eliminar este proyecto?')) {
Inertia.delete(route('projects.destroy', id))
}
}
</script>
<template>
<AppLayout title="Detalle proyecto">
<template #header>
<h1 class="font-semibold text-xl text-gray-800 leading-tight">
Detalle proyecto
</h1>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<h1 class="text-2xl font-bold mb-4">{{ project.name }}</h1>
<p class="text-gray-700 mb-4">
Autor: {{ project.user.name }}
</p>
<p class="text-gray-700 mb-4">{{ project.description }}</p>
<p class="text-gray-700">{{ project.created_at }}</p>
</div>
</div>
</div>
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 mt-4">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<Link :href="route('projects.index')" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded">Volver</Link>
<Link :href="route('projects.edit', project.id)" class="ml-2 bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded">Editar</Link>
<Link
class="ml-2 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
@click="deleteProject(project.id)"
>
Eliminar
</Link>
</div>
</div>
</div>
</div>
</AppLayout>
</template>
CRUD Laravel Inertia: Editar proyectos
Vamos a añadir el método edit a nuestro controlador ProjectController para mostrar una nueva página en Vue que nos permita editar proyectos existentes haciendo uso de nuestro componente Form:
<?php
namespace App\Http\Controllers;
use App\Models\Project;
use Inertia\Response;
class ProjectController extends Controller
{
// ...
public function edit(Project $project): Response
{
return inertia('Projects/Edit', [
'project' => $project,
]);
}Ahora debemos crear la página Edit.vue dentro del directorio Projects de Vue:
<script>
export default {
name: 'ProjectEdit',
}
</script>
<script setup>
import { useForm } from "@inertiajs/inertia-vue3";
import ProjectForm from '@/Components/Projects/Form.vue';
import AppLayout from '@/Layouts/AppLayout.vue';
const props = defineProps({
project: {
type: Object,
required: true,
},
})
const form = useForm({
name: props.project.name,
description: props.project.description,
});
</script>
<template>
<AppLayout title="Editar proyecto">
<template #header>
<h1 class="font-semibold text-xl text-gray-800 leading-tight">
Editar proyecto
</h1>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<ProjectForm
:updating="true"
:form="form"
@submit="form.put(route('projects.update', project.id))"
/>
</div>
</div>
</div>
</div>
</AppLayout>
</template>
CRUD Laravel Inertia: Validar y actualizar proyectos
Ahora, añadamos el método update a nuestro ProjectController para validar y actualizar los proyectos en base de datos:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ProjectRequest;
use App\Models\Project;
class ProjectController extends Controller
{
// ...
public function update(ProjectRequest $request, Project $project)
{
$project->update($request->validated());
return redirect()->route('projects.show', $project);
}Igual que nuestro método store, con la diferencia de que ahora llamamos al método update en lugar de al método create.
CRUD Laravel Inertia: Eliminar proyectos
Para finalizar nuestro CRUD en Laravel Inertia, vamos a añadir el método destroy a nuestro ProjectController, con esto podremos eliminar proyectos y habremos finalizado este tutorial:
<?php
namespace App\Http\Controllers;
use App\Models\Project;
class ProjectController extends Controller
{
// ...
public function destroy(Project $project)
{
$project->delete();
return redirect()->route('projects.index');
}
}Llamando al método delete de nuestro modelo, Eloquent borrará este registro de nuestra base de datos y nuestro trabajo habrá finalizado.
Hasta aquí hemos cubierto cómo crear un CRUD en Laravel Inertia, si quieres seguir mejorando, te invito a que sigas con el curso Laravel Inertia y Vue 3 con arquitectura modular.
Curso Laravel 12
Completo 2026
El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.
star Incluido en cualquier suscripción