Skip to content

Technical SEO for a Laravel and Livewire portfolio: what I actually implemented

A practical checklist from building this site: per-page meta, canonical URLs, Open Graph, JSON-LD, a dynamic sitemap and Search Console — with the code and the things I deliberately skipped.

Gohar Nawaz 4 min read
Laravel and Livewire icons with the title Technical SEO for a Laravel and Livewire portfolio

A portfolio that nobody can find is a brochure in a drawer. When I built this site I treated technical SEO as part of the feature work, not a plugin to add at the end. This article lists what I implemented in a Laravel and Livewire application, in the order that matters most.

Start with the good news: Livewire is server-rendered

A common worry is that "reactive" frameworks hide content from crawlers. Livewire renders full HTML on the server, so the first response already contains your content, headings and links. You do not need a prerender service. That removes the biggest SEO risk of many JavaScript-heavy sites.

1. A title and description for every page

Search results show the <title> and meta description, so each URL needs its own. I use one layout that reads optional variables and falls back to sensible defaults:

@php
    $seoTitle = $title ?? 'Gohar Nawaz | Full Stack Engineer';
    $seoDescription = $description ?? 'Full Stack Engineer building Laravel and Node.js platforms.';
@endphp

<title>{{ $seoTitle }}</title>
<meta name="description" content="{{ $seoDescription }}">

Each Livewire page then passes its own values through the layout:

return view('livewire.article-show')->layout('layouts.app', [
    'title' => $article->seoTitle().' | Gohar Nawaz',
    'description' => $article->seoDescription(),
]);

Keep titles under about 60 characters and descriptions under about 160, or search engines truncate them. Write for a human deciding whether to click.

2. One canonical URL per page

The same content reachable at several URLs splits its ranking signals. Add a canonical link:

<link rel="canonical" href="https://example.com/articles/my-article">

For pages with filters in the query string, such as ?tag=laravel, point the canonical at the clean listing URL so filtered views are not indexed as separate pages.

3. Open Graph and Twitter cards

When a link is pasted into LinkedIn, Slack or WhatsApp, these tags decide how it looks:

<meta property="og:title" content="...">
<meta property="og:description" content="...">
<meta property="og:image" content="https://example.com/og-image.png">
<meta name="twitter:card" content="summary_large_image">

Use an absolute image URL at 1200×630 pixels. Social networks cache aggressively, so after changing the image, ask each platform to re-scrape the page.

4. Structured data with JSON-LD

Structured data tells search engines what a page is. I use:

  • Person and WebSite on every page, so the name, job title and profiles connect to one entity.
  • BlogPosting on articles, with the headline, dates, author and image.
  • BreadcrumbList for the Home → Articles → Article path.
$data = [
    '@context' => 'https://schema.org',
    '@type' => 'BlogPosting',
    'headline' => $article->title,
    'datePublished' => $article->published_at->toIso8601String(),
    'dateModified' => $article->updated_at->toIso8601String(),
    'author' => ['@id' => url('/#person')],
];

echo '<script type="application/ld+json">'.json_encode($data, JSON_UNESCAPED_SLASHES).'</script>';

Validate pages with Google's Rich Results Test. Only mark up what is actually visible on the page.

5. A dynamic sitemap

A static sitemap.xml goes stale the moment you publish something new. Generate it from the database instead:

Route::get('/sitemap.xml', SitemapController::class);

The controller lists the homepage, case studies, the articles index and every published article with its updated_at as lastmod. Cache the result for a few minutes. Also delete any leftover static file from public/ — a real file in that folder is served before Laravel's route ever runs.

6. robots.txt

Keep it short and explicit: block the admin area and private form pages, and point to the sitemap.

User-agent: *
Disallow: /admin
Disallow: /leave-a-review

Sitemap: https://example.com/sitemap.xml

robots.txt stops crawling, not indexing. For pages that must never appear in results, also send noindex — I do this for draft previews and the admin.

7. Content structure

  • One <h1> per page, and <h2>/<h3> in a logical order. The article table of contents is generated from them.
  • Link related pages to each other. Internal links help crawlers discover pages and tell them what each page is about — for instance, this article connects to the hosting setup and the Terraform workflow.
  • Descriptive link text, not "click here".

8. Images

Give every meaningful image alt text, explicit width and height (to avoid layout shift), and loading="lazy" below the fold. Serve modern formats such as WebP. I resize and convert uploads in the browser before they reach the server.

9. Speed and Core Web Vitals

Google uses real-user page experience as a signal. The cheapest wins on this site: no render-blocking third-party scripts, self-hosted fonts, a CSS-only background instead of an image, and loading the syntax highlighter only on pages that contain code.

10. Tell Google you exist

  1. Verify the domain in Google Search Console.
  2. Submit the sitemap.
  3. Use URL Inspection → Request indexing for important new pages.

Be patient: new pages commonly take days or weeks to appear and longer to rank. Publishing useful, specific content regularly matters more than any single tag.

What I deliberately skipped

  • Keyword stuffing and meta keywords — ignored by search engines and bad for readers.
  • Buying links — a quick way to hurt a young site.
  • Chasing a perfect score — fix what is measurably wrong, then go back to writing.

Checklist

Item Done when
Unique title and description Every URL has its own
Canonical Present, filters point to the clean URL
Open Graph Absolute 1200×630 image
JSON-LD Validates in the Rich Results Test
Sitemap Dynamic, submitted in Search Console
robots.txt Blocks admin, links the sitemap
Images Alt text, dimensions, lazy loading
Internal links Related pages link to each other