Laravel 13-tutorial #5: een homepage bouwen met Vite en Tailwind
geverifieerd op 7 september 2026 · 6 min
Laravel 13 levert Vite en Tailwind CSS standaard mee. Draai npm install en daarna npm run dev, en zet @vite(['resources/css/app.css', 'resources/js/app.js']) in je layout. Ontbreekt een van die twee commando's, dan loopt de pagina stuk op “Vite manifest not found”.
Dit hoofdstuk bouwt het visuele geraamte van de site: een gedeelde layout, een homepage en de compilatieketen voor de stylesheets. Artikelen bestaan nog niet, dus de pagina toont een lege lijst, hoofdstuk 6 vult haar.
Vite en Tailwind, meegeleverd met Laravel
Laravel 13 komt met Vite om de assets te compileren en met Tailwind CSS voor de styling. Aan PHP-kant valt er niets te installeren: package.json declareert ze al.
Eerdere versies van deze tutorial laadden Bootstrap 4.5 en jQuery van een CDN. Die aanpak heeft twee nadelen: elke pagina doet er drie requests naar servers van derden bij, en de tooling die Laravel meelevert blijft volledig ongebruikt. Tailwind, gecompileerd door Vite, levert één CSS-bestand op dat alleen de klassen bevat die je werkelijk gebruikt.
Installeer de JavaScript-dependencies, eenmalig:
npm installStart daarna de Vite-ontwikkelserver in een tweede terminal, naast php artisan serve:
npm run devVite houdt je bestanden in de gaten en ververst de browser bij elke wijziging. Voor de definitieve bestanden, bijvoorbeeld vlak voor een deployment:
npm run buildpublic/build/assets/instrument-sans-500-normal-Z6ESRlEs.woff 21.65 kB
public/build/assets/instrument-sans-600-normal-B9e8oLYv.woff 21.67 kB
public/build/assets/fonts-C9MNnjVw.css 2.35 kB │ gzip: 0.38 kB
public/build/assets/app-D8N0vhr5.css 61.08 kB │ gzip: 12.65 kB
public/build/assets/app-BvRk9kiK.js 0.00 kB │ gzip: 0.02 kB
✓ built in 1.43sGeeft de pagina deze fout terug, met het absolute pad van je project:
Vite manifest not found at: /chemin/vers/blog/public/build/manifest.jsondan is noch npm run dev noch npm run build gedraaid. De directive @vite heeft er een van de twee nodig. Dit is in dit stadium de meest voorkomende fout.
De layout van de site
Maak resources/views/layouts/app.blade.php aan. Dat is het geraamte dat alle publieke pagina’s uitbreiden, volgens het overervingsmechanisme uit hoofdstuk 3.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', config('app.name'))</title>
<meta name="description" content="@yield('description', 'Un blog construit avec Laravel 13.')">
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="bg-gray-50 text-gray-900 antialiased">
<header class="border-b bg-white">
<div class="mx-auto flex max-w-5xl items-center justify-between gap-4 px-4 py-4">
<a href="http://%20route('home')%20" class="text-lg font-semibold">{{ config('app.name') }}</a>
<form action="{{ route('search') }}" method="GET" role="search" class="flex gap-2">
<label for="q" class="sr-only">Rechercher</label>
<input id="q" type="search" name="q" value="{{ request('q') }}"
placeholder="Rechercher…"
class="rounded border border-gray-300 px-3 py-1.5 text-sm">
<button type="submit" class="rounded bg-gray-900 px-3 py-1.5 text-sm text-white">Go</button>
</form>
</div>
</header>
<main class="mx-auto grid max-w-5xl gap-8 px-4 py-8 md:grid-cols-3">
<div class="md:col-span-2">
@yield('content')
</div>
@include('partials.sidebar')
</main>
<footer class="border-t bg-white">
<div class="mx-auto max-w-5xl px-4 py-6 text-sm text-gray-500">
{{ config('app.name') }} — tutoriel Laravel 13.
</div>
</footer>
</body>
</html>Drie punten verdienen aandacht. @vite([...]) zet de tags naar de gecompileerde bestanden in de pagina, en wijst afhankelijk van de context naar de ontwikkelserver of naar de gebouwde bestanden. Het zoekformulier gebruikt route('search'), een route die pas in hoofdstuk 8 gedeclareerd wordt, laat het tot dan weg of zet de regel in commentaar. En het <label class="sr-only"> blijft leesbaar voor schermlezers terwijl het onzichtbaar is.
De sidebar
Maak resources/views/partials/sidebar.blade.php aan. Die toont de categorieën, de tags en de recente artikelen, allemaal aangeleverd door de controller.
<aside class="space-y-8">
<section>
<h2 class="mb-2 font-semibold">Catégories</h2>
<ul class="space-y-1 text-sm">
@foreach ($categories as $category)
<li>
<a href="http://%20route('category',%20$category)%20" class="text-blue-700 hover:underline">
{{ $category->name }}
</a>
<span class="text-gray-400">({{ $category->posts_count }})</span>
</li>
@endforeach
</ul>
</section>
<section>
<h2 class="mb-2 font-semibold">Étiquettes</h2>
<ul class="flex flex-wrap gap-2 text-sm">
@foreach ($tags as $tag)
<li>
<a href="http://%20route('tag',%20$tag)%20" class="rounded bg-gray-200 px-2 py-0.5 hover:bg-gray-300">
{{ $tag->name }}
</a>
</li>
@endforeach
</ul>
</section>
<section>
<h2 class="mb-2 font-semibold">Articles récents</h2>
<ul class="space-y-1 text-sm">
@foreach ($recentPosts as $recent)
<li>
<a href="http://%20route('post',%20$recent)%20" class="text-blue-700 hover:underline">{{ $recent->title }}</a>
</li>
@endforeach
</ul>
</section>
</aside>De artikellijst
Deze view wordt straks opgenomen door de homepage, de categorie- en tagpagina’s en de resultatenpagina. Eén keer schrijven scheelt vijf keer corrigeren.
@forelse ($posts as $post)
<article class="mb-6 rounded border bg-white p-4">
<h2 class="text-xl font-semibold">
<a href="http://%20route('post',%20$post)%20" class="hover:underline">{{ $post->title }}</a>
</h2>
<p class="mt-1 text-sm text-gray-500">
{{ $post->user?->name }} —
<a href="http://%20route('category',%20$post-category)%20" class="hover:underline">{{ $post->category?->name }}</a>
@if ($post->published_at) — {{ $post->published_at->translatedFormat('j F Y') }} @endif
</p>
<p class="mt-2 text-gray-700">{{ $post->excerpt ?: Str::limit(strip_tags($post->content), 160) }}</p>
<a href="http://%20route('post',%20$post)%20" class="mt-3 inline-block text-sm text-blue-700 hover:underline">Lire la suite</a>
</article>
@empty
<p class="text-gray-600">Aucun article pour le moment.</p>
@endforelseDe operator ?-> voorkomt een fout als de relatie ontbreekt: $post->user?->name geeft null terug in plaats van te crashen wanneer de auteur verwijderd is. translatedFormat schrijft de datum in de taal van de applicatie, anders dan format, dat in het Engels blijft.
De homepage
@extends('layouts.app')
@section('title', config('app.name'))
@section('content')
@if ($featuredPosts->isNotEmpty())
<section class="mb-8">
<h1 class="mb-3 text-2xl font-bold">À la une</h1>
<ul class="space-y-1">
@foreach ($featuredPosts as $featured)
<li>
<a href="http://%20route('post',%20$featured)%20" class="text-blue-700 hover:underline">{{ $featured->title }}</a>
</li>
@endforeach
</ul>
</section>
@endif
<h2 class="mb-4 text-xl font-bold">Derniers articles</h2>
@include('partials.posts-list')
@endsectionDe controller
De controller van de homepage kan nog geen modellen bevragen, want die bestaan niet. Geef hem lege collecties mee om de layout nu al te kunnen bekijken:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Collection;
use Illuminate\View\View;
class IndexController extends Controller
{
public function __invoke(): View
{
return view('home', [
'posts' => new Collection(),
'featuredPosts' => new Collection(),
'categories' => new Collection(),
'tags' => new Collection(),
'recentPosts' => new Collection(),
]);
}
}<?php
use App\Http\Controllers\IndexController;
use Illuminate\Support\Facades\Route;
Route::get('/', IndexController::class)->name('home');Herlaad de pagina: de header, de sidebar en de footer verschijnen, met “Aucun article pour le moment” in het midden. De structuur staat, alleen de data ontbreekt.
Het volgende hoofdstuk maakt de tabellen, de modellen en de back-officeschermen die deze pagina gaan vullen.