Agency websites have a specific problem: they need to look impressive enough to win clients, while being fast enough to rank in search. Those two goals are often in tension.
A visually rich WordPress site with a premium theme, a handful of plugins, and a page builder can look excellent. It can also have a Largest Contentful Paint over 4 seconds and a PageSpeed score in the 50s — which, for a web design agency, is a quiet form of professional embarrassment.
This is the story of why I migrated CenoDigital's website to Astro, how I approached it, and what the performance difference looked like.
The Problem With the Previous Setup
The original site was on WordPress with a premium theme. Clients responded positively to it. But the performance metrics weren't where they needed to be, and the issues were structural — not just configuration problems.
- Render-blocking JavaScript from the page builder and theme scripts
- Inconsistent image handling — the theme's responsive image support was unreliable
- Plugin overhead — every additional plugin adds weight, even small ones
- No static generation — every page load triggered a database query and PHP render cycle
For a marketing site where every page is essentially static content, this architecture is overkill. There's no reason a web design agency's "Services" page should be dynamically rendered on every visit.
Why Astro
I evaluated a few options before deciding:
| Framework | Verdict |
|---|---|
| Next.js | Familiar but over-engineered for a content site — React overhead without the benefit |
| Gatsby | Good performance, but ecosystem has lost momentum |
| Astro | Ships zero JS by default — the right call for a marketing site |
Astro's core design decision is the right one for this use case: components only hydrate when they explicitly need client-side interactivity. For a portfolio and services site, most components don't need that at all.
The Migration Approach
I didn't try to replicate the WordPress site pixel-for-pixel. Migration was an opportunity to audit the content architecture and simplify.
Step 1: Content audit. Every page was reviewed and cut if it wasn't pulling weight. Service pages were consolidated. The portfolio section was restructured.
Step 2: Design in code, not in a builder. No page builder, no shortcodes. Every layout decision is intentional and every line of CSS is there for a reason.
Step 3: Image optimisation. Astro's built-in <Image /> component handles WebP conversion, responsive sizing, and lazy loading automatically.
--- import { Image } from 'astro:assets'; import type { CollectionEntry } from 'astro:content'; interface Props { project: CollectionEntry<'portfolio'>; } const { project } = Astro.props; --- <article class="project-card"> <Image src={project.data.cover} alt={project.data.title} width={800} height={450} format="webp" quality={80} /> <h2>{project.data.title}</h2> </article>
Step 4: Content Collections for the blog. Posts are MDX files in src/content/blog/ — no CMS database, no plugin dependencies, version-controlled alongside the code.
--- import { getCollection } from 'astro:content'; export async function getStaticPaths() { const posts = await getCollection('blog'); return posts.map(post => ({ params: { slug: post.slug }, props: { post }, })); } const { post } = Astro.props; const { Content } = await post.render(); --- <article> <h1>{post.data.title}</h1> <Content /> </article>
Step 5: Deployment via Coolify. The old site was on managed WordPress hosting. The new site deploys from a Git push to a Coolify instance on Contabo. Build time is under 30 seconds.
The Performance Numbers
Here's what PageSpeed Insights shows before and after:
| Metric | WordPress | Astro |
|---|---|---|
| Performance Score | 54 | 97 |
| Largest Contentful Paint | 4.2s | 0.9s |
| Total Blocking Time | 380ms | 0ms |
| Cumulative Layout Shift | 0.14 | 0.01 |
| First Contentful Paint | 2.8s | 0.6s |
The Total Blocking Time going to 0ms is the result I'm most satisfied with. That's the direct result of shipping no unnecessary JavaScript.
SEO Impact
Performance is only part of the SEO story. The migration was also an opportunity to rebuild the on-page SEO properly for the terms CenoDigital targets:
- Clear page hierarchy with descriptive H1s and H2s
- Schema markup (LocalBusiness + WebPage types)
- Canonical URLs on all pages
- Sitemap auto-generated by Astro's sitemap integration
- Open Graph and Twitter card meta on every page
I also rewrote the service pages — replacing vague marketing copy with specific explanations of process, deliverables, and expected outcomes. Longer, more substantive content that's actually useful to someone evaluating an agency.
Is Astro Right for Every Site?
No. Astro is excellent for content-heavy sites with minimal dynamic requirements — blogs, portfolios, marketing sites, documentation. If you're building something with complex auth flows or real-time features, the lack of React-as-default becomes a constraint.
For a digital agency's marketing site, though, it's close to the ideal tool. Fast by default, flexible when you need it, and opinionated in exactly the right direction.