Skip to content

Commit 6c15fd8

Browse files
roncodesclaude
andcommitted
feat(seo): Medium + Low priority SEO pass (audit items 11-21)
Tackles every Medium and Low audit item except #15 (unused video cleanup) and #16 (images.formats explicit config), which were explicitly out of scope this round. Item by item: - #11 Visual breadcrumbs: new Breadcrumbs component at components/ui/breadcrumbs.tsx that emits both the visible <nav> trail and matching BreadcrumbList JSON-LD in one call. Wired into all 3 compare pages and 14 solutions pages (8 verticals + 6 use cases) via a new optional `breadcrumbs` prop on the shared SolutionPageLayout. - #12 Per-page Twitter cards: 41 pages now declare their own twitter.card block mirroring the page's openGraph title and description (previously every page inherited the site-wide default Twitter card, so social previews were identical regardless of URL). - #13 Dynamic OG images: new /og route using next/og ImageResponse, parameterised by title/eyebrow/subtitle, returns a 1200x630 PNG with branded gradient and the Fleetbase logomark. Cached one year via immutable Cache-Control. Wired into the homepage, /pricing, /developers, all 3 compare pages, and blog posts (falls back to the dynamic OG when Ghost has no feature image). - #14 Generic anchor text: replaced "Learn more" / "Read more" / "Learn More" with contextual anchors that include the destination's name (Explore Fleet-Ops, View Installation Service, Read the Fleet-Ops docs, etc.) across platform/solutions/services/pricing/ licensing/platform-modules. Dead about-news component left alone. - #17 next/dynamic for heavy components: the 7 below-the-fold homepage sections (FleetOpsShowcase, OpenSourceAdvantage, FeaturesTabsSection, IndustryUseCases, IntegrationsEcosystem, DeploymentOptions, FinalCTA) are now dynamically imported. SSR stays on (default) so crawlers still receive complete HTML; only the client JS bundle is split. Verified each dynamic section's text is present in the SSR'd HTML response. - #18 Compare-page competitor schema: each compare page now emits a SoftwareApplication JSON-LD entity for the competitor (Onfleet, Tookan, Route4Me) in addition to the BreadcrumbList. Helps Google understand the page as a comparison between two distinct entities. - #19 Hero <header> elements: replaced <section> with <header> on the three real page-hero components (Hero, ProductHero, AboutHero). Section heroes inside content remain <section>. - #20 googleBot robots block: extended root metadata.robots with googleBot directives — max-image-preview:large, max-snippet:-1, max-video-preview:-1 — to opt into the largest possible SERP preview. - #21 Trim keywords meta: the root keywords array went from 19 generic terms to 6 focused, high-intent terms. Google ignores the keywords meta entirely so this was purely cosmetic SEO hygiene. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9377ac3 commit 6c15fd8

59 files changed

Lines changed: 756 additions & 68 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/app/blog/[slug]/page.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,19 @@ export async function generateMetadata(props: {
4040
}
4141

4242
const canonicalUrl = `${BASE_URL}/blog/${post.slug}`;
43-
const image = post.featureImage
44-
? [
45-
{
46-
url: post.featureImage,
47-
alt: post.featureImageAlt || post.title,
48-
},
49-
]
50-
: undefined;
43+
44+
// Prefer the post's feature image; otherwise fall back to a branded
45+
// dynamic OG image built from the post title + excerpt so every post
46+
// gets a unique social card instead of the site-wide default.
47+
const fallbackOg = `${BASE_URL}/og?title=${encodeURIComponent(post.title)}&eyebrow=Blog${
48+
post.excerpt ? `&subtitle=${encodeURIComponent(post.excerpt)}` : ''
49+
}`;
50+
const image = [
51+
{
52+
url: post.featureImage || fallbackOg,
53+
alt: post.featureImageAlt || post.title,
54+
},
55+
];
5156

5257
return {
5358
title: post.title,
@@ -66,10 +71,10 @@ export async function generateMetadata(props: {
6671
tags: post.tags.map((tag) => tag.name),
6772
},
6873
twitter: {
69-
card: image ? 'summary_large_image' : 'summary',
74+
card: 'summary_large_image',
7075
title: post.title,
7176
description: post.excerpt,
72-
images: image?.map((item) => item.url),
77+
images: image.map((item) => item.url),
7378
},
7479
};
7580
}

src/app/blog/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ export const metadata: Metadata = {
2222
description:
2323
'Product updates, engineering deep-dives, and logistics industry insights from Fleetbase.',
2424
},
25+
twitter: {
26+
card: 'summary_large_image',
27+
title: `Blog | Fleetbase`,
28+
description: `Product updates, engineering deep-dives, and logistics industry insights from Fleetbase.`,
29+
},
2530
};
2631

2732
export const revalidate = 300;

src/app/community/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export const metadata: Metadata = {
1313
title: 'Community | Fleetbase',
1414
description: 'Join thousands of logistics operators and developers in the Fleetbase community.',
1515
},
16+
twitter: {
17+
card: 'summary_large_image',
18+
title: `Community | Fleetbase`,
19+
description: `Join thousands of logistics operators and developers in the Fleetbase community.`,
20+
},
1621
};
1722

1823
const channels = [

src/app/company/about/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ export const metadata: Metadata = {
1414
title: 'About Us | Fleetbase',
1515
description: 'The open-source logistics platform on a mission to democratize logistics technology.',
1616
},
17+
twitter: {
18+
card: 'summary_large_image',
19+
title: `About Us | Fleetbase`,
20+
description: `The open-source logistics platform on a mission to democratize logistics technology.`,
21+
},
1722
};
1823

1924
const values = [

src/app/company/investors/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ export const metadata: Metadata = {
3535
description:
3636
'Bootstrapped open-source logistics platform with 8,000+ active instances. Learn about the opportunity and what we look for in long-term partners.',
3737
},
38+
twitter: {
39+
card: 'summary_large_image',
40+
title: `Investors | Fleetbase`,
41+
description: `Bootstrapped open-source logistics platform with 8,000+ active instances. Learn about the opportunity and what we look for in long-term partners.`,
42+
},
3843
alternates: { canonical: 'https://fleetbase.io/company/investors' },
3944
};
4045

src/app/company/open-source/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export const metadata: Metadata = {
1313
title: 'Open Source Mission | Fleetbase',
1414
description: 'Fully open-source logistics infrastructure under AGPL-3.0. Deploy anywhere, own everything.',
1515
},
16+
twitter: {
17+
card: 'summary_large_image',
18+
title: `Open Source Mission | Fleetbase`,
19+
description: `Fully open-source logistics infrastructure under AGPL-3.0. Deploy anywhere, own everything.`,
20+
},
1621
};
1722

1823
const reasons = [

src/app/company/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ export const metadata: Metadata = {
1212
title: 'Company | Fleetbase',
1313
description: 'Open-source logistics infrastructure built to democratize logistics technology.',
1414
},
15+
twitter: {
16+
card: 'summary_large_image',
17+
title: `Company | Fleetbase`,
18+
description: `Open-source logistics infrastructure built to democratize logistics technology.`,
19+
},
1520
};
1621

1722
const companyLinks = [

src/app/compare/vs-onfleet/page.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import type { Metadata } from 'next';
22
import Link from 'next/link';
33
import { CheckCircle2, XCircle, ArrowRight, Minus } from 'lucide-react';
44
import { Button } from '@/components/ui/button';
5-
import { BreadcrumbSchema } from '@/components/seo/json-ld';
5+
import { Breadcrumbs } from '@/components/ui/breadcrumbs';
6+
import { SoftwareApplicationSchema } from '@/components/seo/json-ld';
67

78
export const metadata: Metadata = {
89
title: 'Fleetbase vs Onfleet | Open-Source Onfleet Alternative',
@@ -21,6 +22,22 @@ export const metadata: Metadata = {
2122
title: 'Fleetbase vs Onfleet | Open-Source Onfleet Alternative',
2223
description:
2324
'Compare Fleetbase vs Onfleet. No per-task pricing, self-hosting, full API access, and a free tier. The open-source alternative to Onfleet.',
25+
images: [
26+
{
27+
url: '/og?title=Fleetbase%20vs%20Onfleet&eyebrow=Compare&subtitle=The%20open-source%20alternative%20to%20Onfleet%20%E2%80%94%20no%20per-task%20pricing%2C%20self-hosted%20or%20cloud.',
28+
width: 1200,
29+
height: 630,
30+
alt: 'Fleetbase vs Onfleet — open-source Onfleet alternative',
31+
},
32+
],
33+
},
34+
twitter: {
35+
card: 'summary_large_image',
36+
title: 'Fleetbase vs Onfleet',
37+
description: 'The open-source Onfleet alternative — no per-task pricing, self-hosted or cloud.',
38+
images: [
39+
'/og?title=Fleetbase%20vs%20Onfleet&eyebrow=Compare&subtitle=The%20open-source%20alternative%20to%20Onfleet%20%E2%80%94%20no%20per-task%20pricing%2C%20self-hosted%20or%20cloud.',
40+
],
2441
},
2542
alternates: { canonical: 'https://fleetbase.io/compare/vs-onfleet' },
2643
};
@@ -68,17 +85,22 @@ function Cell({ value }: { value: string | boolean }) {
6885
export default function VsOnfleetPage() {
6986
return (
7087
<div className="min-h-screen">
71-
<BreadcrumbSchema
72-
items={[
73-
{ name: 'Home', url: 'https://fleetbase.io' },
74-
{ name: 'Compare', url: 'https://fleetbase.io/compare' },
75-
{ name: 'Fleetbase vs Onfleet', url: 'https://fleetbase.io/compare/vs-onfleet' },
76-
]}
88+
<SoftwareApplicationSchema
89+
name="Onfleet"
90+
url="https://onfleet.com"
91+
description="Last-mile delivery management software for dispatch, routing, and proof of delivery — closed-source, per-task pricing."
7792
/>
7893

7994
{/* Hero */}
8095
<section className="section-padding border-b bg-gradient-to-b from-muted/30 to-background">
8196
<div className="container max-w-5xl text-center space-y-6">
97+
<Breadcrumbs
98+
className="justify-center [&_ol]:justify-center"
99+
items={[
100+
{ label: 'Compare', href: '/compare' },
101+
{ label: 'Fleetbase vs Onfleet', href: '/compare/vs-onfleet' },
102+
]}
103+
/>
82104
<div className="inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium text-muted-foreground">
83105
Comparison
84106
</div>

src/app/compare/vs-route4me/page.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import type { Metadata } from 'next';
22
import Link from 'next/link';
33
import { CheckCircle2, XCircle, ArrowRight } from 'lucide-react';
44
import { Button } from '@/components/ui/button';
5-
import { BreadcrumbSchema } from '@/components/seo/json-ld';
5+
import { SoftwareApplicationSchema } from '@/components/seo/json-ld';
6+
import { Breadcrumbs } from '@/components/ui/breadcrumbs';
67

78
export const metadata: Metadata = {
89
title: 'Fleetbase vs Route4Me | Open-Source Route4Me Alternative',
@@ -21,6 +22,22 @@ export const metadata: Metadata = {
2122
title: 'Fleetbase vs Route4Me | Open-Source Route4Me Alternative',
2223
description:
2324
'Compare Fleetbase vs Route4Me. Full dispatch, fleet management, driver app, and route optimization — no per-route fees.',
25+
images: [
26+
{
27+
url: '/og?title=Fleetbase%20vs%20Route4Me&eyebrow=Compare&subtitle=Open-source%20alternative%20with%20dispatch%2C%20fleet%20management%2C%20driver%20app%2C%20and%20route%20optimization%20built%20in.',
28+
width: 1200,
29+
height: 630,
30+
alt: 'Fleetbase vs Route4Me — open-source Route4Me alternative',
31+
},
32+
],
33+
},
34+
twitter: {
35+
card: 'summary_large_image',
36+
title: 'Fleetbase vs Route4Me',
37+
description: 'Open-source alternative with dispatch, fleet management, driver app, and route optimization built in.',
38+
images: [
39+
'/og?title=Fleetbase%20vs%20Route4Me&eyebrow=Compare&subtitle=Open-source%20alternative%20with%20dispatch%2C%20fleet%20management%2C%20driver%20app%2C%20and%20route%20optimization%20built%20in.',
40+
],
2441
},
2542
alternates: { canonical: 'https://fleetbase.io/compare/vs-route4me' },
2643
};
@@ -68,17 +85,22 @@ function Cell({ value }: { value: string | boolean }) {
6885
export default function VsRoute4MePage() {
6986
return (
7087
<div className="min-h-screen">
71-
<BreadcrumbSchema
72-
items={[
73-
{ name: 'Home', url: 'https://fleetbase.io' },
74-
{ name: 'Compare', url: 'https://fleetbase.io/compare' },
75-
{ name: 'Fleetbase vs Route4Me', url: 'https://fleetbase.io/compare/vs-route4me' },
76-
]}
88+
<SoftwareApplicationSchema
89+
name="Route4Me"
90+
url="https://route4me.com"
91+
description="Route planning and optimization software focused on last-mile delivery routing — closed-source SaaS."
7792
/>
7893

7994
{/* Hero */}
8095
<section className="section-padding border-b bg-gradient-to-b from-muted/30 to-background">
8196
<div className="container max-w-5xl text-center space-y-6">
97+
<Breadcrumbs
98+
className="justify-center [&_ol]:justify-center"
99+
items={[
100+
{ label: 'Compare', href: '/compare' },
101+
{ label: 'Fleetbase vs Route4Me', href: '/compare/vs-route4me' },
102+
]}
103+
/>
82104
<div className="inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium text-muted-foreground">
83105
Comparison
84106
</div>

src/app/compare/vs-tookan/page.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import type { Metadata } from 'next';
22
import Link from 'next/link';
33
import { CheckCircle2, XCircle, ArrowRight, Minus } from 'lucide-react';
44
import { Button } from '@/components/ui/button';
5-
import { BreadcrumbSchema } from '@/components/seo/json-ld';
5+
import { SoftwareApplicationSchema } from '@/components/seo/json-ld';
6+
import { Breadcrumbs } from '@/components/ui/breadcrumbs';
67

78
export const metadata: Metadata = {
89
title: 'Fleetbase vs Tookan | Open-Source Tookan Alternative',
@@ -21,6 +22,22 @@ export const metadata: Metadata = {
2122
title: 'Fleetbase vs Tookan | Open-Source Tookan Alternative',
2223
description:
2324
'Compare Fleetbase vs Tookan. No agent-based pricing, self-hosting, full API access. The open-source alternative to Tookan.',
25+
images: [
26+
{
27+
url: '/og?title=Fleetbase%20vs%20Tookan&eyebrow=Compare&subtitle=The%20open-source%20alternative%20to%20Tookan%20%E2%80%94%20no%20agent-based%20pricing%2C%20self-hosted%20or%20cloud.',
28+
width: 1200,
29+
height: 630,
30+
alt: 'Fleetbase vs Tookan — open-source Tookan alternative',
31+
},
32+
],
33+
},
34+
twitter: {
35+
card: 'summary_large_image',
36+
title: 'Fleetbase vs Tookan',
37+
description: 'The open-source Tookan alternative — no agent-based pricing, self-hosted or cloud.',
38+
images: [
39+
'/og?title=Fleetbase%20vs%20Tookan&eyebrow=Compare&subtitle=The%20open-source%20alternative%20to%20Tookan%20%E2%80%94%20no%20agent-based%20pricing%2C%20self-hosted%20or%20cloud.',
40+
],
2441
},
2542
alternates: { canonical: 'https://fleetbase.io/compare/vs-tookan' },
2643
};
@@ -68,17 +85,22 @@ function Cell({ value }: { value: string | boolean }) {
6885
export default function VsTookanPage() {
6986
return (
7087
<div className="min-h-screen">
71-
<BreadcrumbSchema
72-
items={[
73-
{ name: 'Home', url: 'https://fleetbase.io' },
74-
{ name: 'Compare', url: 'https://fleetbase.io/compare' },
75-
{ name: 'Fleetbase vs Tookan', url: 'https://fleetbase.io/compare/vs-tookan' },
76-
]}
88+
<SoftwareApplicationSchema
89+
name="Tookan"
90+
url="https://tookanapp.com"
91+
description="Delivery management and dispatch software with per-agent and per-task pricing — closed-source SaaS."
7792
/>
7893

7994
{/* Hero */}
8095
<section className="section-padding border-b bg-gradient-to-b from-muted/30 to-background">
8196
<div className="container max-w-5xl text-center space-y-6">
97+
<Breadcrumbs
98+
className="justify-center [&_ol]:justify-center"
99+
items={[
100+
{ label: 'Compare', href: '/compare' },
101+
{ label: 'Fleetbase vs Tookan', href: '/compare/vs-tookan' },
102+
]}
103+
/>
82104
<div className="inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium text-muted-foreground">
83105
Comparison
84106
</div>

0 commit comments

Comments
 (0)