How we revamped a legacy Laravel Blade marketing blog, unlocked hidden articles, implemented real-time search, responsive pagination, and an intelligent recommendation engine.
Content engines drive organic user acquisition for SaaS platforms. However, marketing blogs built in early startup stages often suffer from technical debt and abandoned UI features.
In our platform, the legacy blog was on hold: it consisted only of a basic homepage and single article view. Critical navigation features — such as the "Load More" button — were commented out in the codebase, restricting user access to older articles!
Here is how we refactored and migrated the legacy Laravel Blade blog into a modern Vue 3 application complete with dedicated article listings, real-time search, responsive pagination, tag filtering, and a related article recommendation engine.
To solve the dead-end navigation issue, we established a dedicated /articles route and built an asynchronous pagination handler replacing commented legacy code.
class=class="text-emerald-500 dark:text-emerald-400 font-medium">"text-slate-500 italic">// router/index.js
{
path: '/blog',
component: BlogHomeView,
},
{
path: '/blog/articles',
component: ArticleListingView,
},
{
path: '/blog/articles/:slug',
component: ArticleDetailView,
}We built a reactive listing component that filters articles dynamically by title, tag, or category with debounced API queries to prevent unnecessary server requests.
<!-- components/ArticleListingView.vue -->
<template>
<div class="max-w-6xl mx-auto py-12 px-4">
<!-- Search Bar & Tag Filter Pills -->
<div class="flex flex-col md:flex-row justify-between gap-4 mb-8">
<input
v-model="searchQuery"
@input="debouncedSearch"
placeholder="Search articles by keyword or tag..."
class="w-full md:w-96 border rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<div class="flex flex-wrap gap-2">
<button
v-for="tag in availableTags"
:key="tag"
@click="toggleTag(tag)"
:class="selectedTag === tag ? 'bg-blue-600 text-white' : 'bg-slate-100 text-slate-700'"
class="px-3 py-1.5 rounded-lg text-xs font-semibold uppercase"
>
#{{ tag }}
</button>
</div>
</div>
<!-- Article Cards Grid -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<article v-for="article in paginatedArticles" :key="article.id" class="border rounded-2xl p-6 shadow-sm">
<span class="text-xs font-bold uppercase text-blue-600 mb-2 block">{{ article.category }}</span>
<h3 class="text-lg font-bold mb-2">{{ article.title }}</h3>
<p class="text-xs text-slate-500 line-clamp-3 mb-4">{{ article.excerpt }}</p>
<router-link :to="class="text-emerald-500 dark:text-emerald-400 font-medium">`/blog/articles/${article.slug}`" class="text-xs font-bold text-blue-600 hover:underline">
Read Article →
</router-link>
</article>
</div>
</div>
</template>To increase reader engagement, we implemented an algorithm on the article detail view that calculates topic relevance based on shared tags and category overlap: