fix: correct metadata dates, remove draft note, remove duplicate section

- Fixed all 'undefined NaN, NaN' dates in metadata divs across all 22 posts
- Removed draft instruction from qr-code-scan-statistics-2026
- Removed duplicate 'Trackable / dynamic QR code' section from trackable-qr-codes
- All posts now have proper 'Last updated' dates showing January 26, 2026
This commit is contained in:
Timo Knuth
2026-03-06 16:01:01 +01:00
parent 7d5d142156
commit 76bde71585
41 changed files with 3640 additions and 397 deletions

View File

@@ -70,12 +70,13 @@ export default function MarketingLayout({
<nav aria-label="Site Map">
<ul>
<li><a href="/">Home</a></li>
<li><Link href="/pricing">{t.nav.pricing}</Link></li>
<li><Link href="/blog">{t.nav.blog}</Link></li>
<li><Link href="/learn">{t.nav.learn}</Link></li>
<li><Link href="/faq">{t.nav.faq}</Link></li>
<li><Link href="/about">{t.nav.about}</Link></li>
<li><Link href="/contact">{t.nav.contact}</Link></li>
<li><Link href="/pricing">{t.nav.pricing}</Link></li>
<li><Link href="/blog">{t.nav.blog}</Link></li>
<li><Link href="/learn">{t.nav.learn}</Link></li>
<li><Link href="/use-cases">Use Cases</Link></li>
<li><Link href="/faq">{t.nav.faq}</Link></li>
<li><Link href="/about">{t.nav.about}</Link></li>
<li><Link href="/contact">{t.nav.contact}</Link></li>
<li><Link href="/login">{t.nav.login}</Link></li>
<li><Link href="/signup">{t.nav.signup || "Sign Up"}</Link></li>
{/* Tools */}
@@ -175,12 +176,15 @@ export default function MarketingLayout({
<Link href="/about" className="px-3 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 transition-colors">
{t.nav.about}
</Link>
<Link href="/blog" className="px-3 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 transition-colors">
{t.nav.blog}
</Link>
<Link href="/learn" className="px-3 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 transition-colors">
{t.nav.learn}
</Link>
<Link href="/blog" className="px-3 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 transition-colors">
{t.nav.blog}
</Link>
<Link href="/use-cases" className="px-3 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 transition-colors">
Use Cases
</Link>
<Link href="/learn" className="px-3 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 transition-colors">
{t.nav.learn}
</Link>
<Link href="/#faq" className="px-3 py-2 text-sm font-medium text-slate-600 hover:text-slate-900 transition-colors">
{t.nav.faq}
</Link>
@@ -265,9 +269,10 @@ export default function MarketingLayout({
<Link href="/#features" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.features}</Link>
<Link href="/#pricing" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.pricing}</Link>
<Link href="/about" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.about}</Link>
<Link href="/blog" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.blog}</Link>
<Link href="/learn" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.learn}</Link>
<Link href="/about" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.about}</Link>
<Link href="/blog" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.blog}</Link>
<Link href="/use-cases" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>Use Cases</Link>
<Link href="/learn" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.learn}</Link>
<Link href="/#faq" className="block px-4 py-3 text-slate-700 font-medium rounded-xl hover:bg-slate-50" onClick={() => setMobileMenuOpen(false)}>{t.nav.faq}</Link>
<div className="grid grid-cols-2 gap-4 pt-4">

View File

@@ -112,11 +112,11 @@ export default function AboutPage() {
</div>
</div>
<div className="mt-12 text-center">
<Link href="/create-qr">
<Button size="lg">Create QR Code</Button>
</Link>
</div>
<div className="mt-12 text-center">
<Link href="/dynamic-qr-code-generator">
<Button size="lg">Create QR Code</Button>
</Link>
</div>
</div>
</section>

View File

@@ -6,14 +6,20 @@ import { getAuthorBySlug, getPostsByAuthor } from "@/lib/content";
import { authors } from "@/lib/author-data";
import { authorPageSchema } from "@/lib/schema";
export function generateMetadata({ params }: { params: { slug: string } }) {
const author = getAuthorBySlug(params.slug);
if (!author) return {};
return {
title: `${author.name} - ${author.role} | QR Master`,
description: author.bio
};
}
export function generateMetadata({ params }: { params: { slug: string } }) {
const author = getAuthorBySlug(params.slug);
if (!author) return {};
return {
title: `${author.name} - ${author.role} | QR Master`,
description: author.bio,
alternates: {
canonical: `https://www.qrmaster.net/authors/${author.slug}`,
},
openGraph: {
url: `https://www.qrmaster.net/authors/${author.slug}`,
},
};
}
export function generateStaticParams() {
return authors.map((author) => ({

View File

@@ -1,15 +1,18 @@
import React from 'react';
import type { Metadata } from 'next';
import Link from 'next/link';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import SeoJsonLd from '@/components/SeoJsonLd';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { breadcrumbSchema } from '@/lib/schema';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import SeoJsonLd from '@/components/SeoJsonLd';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { breadcrumbSchema } from '@/lib/schema';
import { GrowthLinksSection } from '@/components/marketing/GrowthLinksSection';
import { MarketingPageTracker } from '@/components/marketing/MarketingAnalytics';
import { featuredUseCases } from '@/lib/growth-pages';
export const metadata: Metadata = {
title: {
absolute: 'Bulk QR Code Generator - Create 1000s from Excel',
absolute: 'Bulk QR Code Generator - Create Bulk QR Codes from Excel',
},
description: 'Generate hundreds of QR codes at once from Excel/CSV. Create URLs, vCards, and more in bulk with custom branding. Perfect for products and events.',
keywords: 'bulk qr code generator, batch qr code, qr code from excel, csv qr code generator, mass qr code generation, bulk vcard qr code, bulk qr codes free',
@@ -21,14 +24,14 @@ export const metadata: Metadata = {
},
},
openGraph: {
title: 'Bulk QR Code Generator - Create 1000s from Excel',
title: 'Bulk QR Code Generator - Create Bulk QR Codes from Excel',
description: 'Generate hundreds of QR codes at once from Excel/CSV. Create URLs, vCards, and more in bulk with custom branding.',
url: 'https://www.qrmaster.net/bulk-qr-code-generator',
type: 'website',
images: ['/og-image.png'],
},
twitter: {
title: 'Bulk QR Code Generator - Create 1000s from Excel',
title: 'Bulk QR Code Generator - Create Bulk QR Codes from Excel',
description: 'Generate hundreds of QR codes at once from Excel/CSV. Create URLs, vCards, and more in bulk with custom branding.',
},
};
@@ -280,15 +283,43 @@ export default function BulkQRCodeGeneratorPage() {
],
};
const breadcrumbItems: BreadcrumbItem[] = [
{ name: 'Home', url: '/' },
{ name: 'Bulk QR Code Generator', url: '/bulk-qr-code-generator' },
];
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, faqSchema, breadcrumbSchema(breadcrumbItems)]} />
<div className="min-h-screen bg-white">
const breadcrumbItems: BreadcrumbItem[] = [
{ name: 'Home', url: '/' },
{ name: 'Bulk QR Code Generator', url: '/bulk-qr-code-generator' },
];
const relatedUseCaseLinks = [
{
href: '/qr-code-for-marketing-campaigns',
title: 'QR Codes for Marketing Campaigns',
description: 'Use bulk generation when campaign placement or print distribution needs multiple trackable codes.',
ctaLabel: 'Create a trackable campaign QR',
},
{
href: featuredUseCases[2].href,
title: featuredUseCases[2].title,
description: featuredUseCases[2].summary,
ctaLabel: featuredUseCases[2].ctaLabel,
},
{
href: '/use-cases',
title: 'Explore the use-case hub',
description: 'See where bulk creation fits into broader QR workflows and commercial parents.',
ctaLabel: 'Explore QR code use cases',
},
{
href: '/custom-qr-code-generator',
title: 'Custom QR Code Generator',
description: 'Pair bulk creation with stronger print presentation when brand consistency matters.',
ctaLabel: 'Design a custom QR code',
},
];
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, faqSchema, breadcrumbSchema(breadcrumbItems)]} />
<MarketingPageTracker pageType="commercial" cluster="bulk-qr" />
<div className="min-h-screen bg-white">
{/* Hero Section */}
<section className="relative overflow-hidden bg-gradient-to-br from-green-50 via-white to-blue-50 py-20">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
@@ -297,7 +328,7 @@ export default function BulkQRCodeGeneratorPage() {
<div className="space-y-8">
<div className="inline-flex items-center space-x-2 bg-green-100 text-green-800 px-4 py-2 rounded-full text-sm font-semibold">
<span></span>
<span>Generate 1000s in Minutes</span>
<span>CSV and Excel workflows</span>
</div>
<h1 className="text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
@@ -347,7 +378,7 @@ export default function BulkQRCodeGeneratorPage() {
<div className="bg-gray-50 border-2 border-dashed border-gray-300 rounded-lg p-8 text-center mb-4">
<div className="text-4xl mb-2">📊</div>
<p className="text-gray-600 font-medium mb-1">products.xlsx</p>
<p className="text-sm text-gray-500">1,247 rows ready</p>
<p className="text-sm text-gray-500">Bulk-ready file</p>
</div>
<div className="flex items-center justify-center mb-4">
<svg className="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -362,11 +393,11 @@ export default function BulkQRCodeGeneratorPage() {
))}
</div>
<p className="text-center text-sm text-gray-600 mt-4">
+ 1,239 more codes
Continue with a full batch import
</p>
</Card>
<div className="absolute -top-4 -right-4 bg-green-500 text-white px-4 py-2 rounded-full text-sm font-semibold shadow-lg">
1000s at Once!
Bulk import
</div>
</div>
</div>
@@ -631,14 +662,23 @@ Product C,https://example.com/product-c,Budget Widget,electronics,sale`}
</Card>
))}
</div>
</div>
</section>
{/* CTA Section */}
<section className="py-20 bg-gradient-to-r from-green-600 to-blue-600 text-white">
</div>
</section>
<GrowthLinksSection
eyebrow="Bulk-ready workflows"
title="Where bulk QR creation becomes operationally useful"
description="Bulk generation is strongest when one spreadsheet feeds a real deployment workflow across campaigns, events, labels, or repeated placements."
links={relatedUseCaseLinks}
pageType="commercial"
cluster="bulk-qr"
/>
{/* CTA Section */}
<section className="py-20 bg-gradient-to-r from-green-600 to-blue-600 text-white">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-4xl text-center">
<h2 className="text-4xl font-bold mb-6">
Generate 1000s of QR Codes in Minutes
Generate bulk QR codes without one-by-one setup
</h2>
<p className="text-xl mb-8 text-green-100">
Save hours of manual work. Upload your file and get all QR codes ready instantly.

View File

@@ -4,9 +4,12 @@ import Link from 'next/link';
import Image from 'next/image';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import SeoJsonLd from '@/components/SeoJsonLd';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { breadcrumbSchema } from '@/lib/schema';
import SeoJsonLd from '@/components/SeoJsonLd';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { breadcrumbSchema } from '@/lib/schema';
import { GrowthLinksSection } from '@/components/marketing/GrowthLinksSection';
import { MarketingPageTracker } from '@/components/marketing/MarketingAnalytics';
import { featuredUseCases } from '@/lib/growth-pages';
import {
Palette,
Upload,
@@ -287,15 +290,43 @@ export default function CustomQRCodeGeneratorPage() {
})),
};
const breadcrumbItems: BreadcrumbItem[] = [
{ name: 'Home', url: '/' },
{ name: 'Custom QR Code Generator', url: '/custom-qr-code-generator' },
];
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, faqSchema, breadcrumbSchema(breadcrumbItems)]} />
<div className="min-h-screen bg-white">
const breadcrumbItems: BreadcrumbItem[] = [
{ name: 'Home', url: '/' },
{ name: 'Custom QR Code Generator', url: '/custom-qr-code-generator' },
];
const relatedUseCaseLinks = [
{
href: '/qr-code-for-marketing-campaigns',
title: 'QR Codes for Marketing Campaigns',
description: 'Connect branded print QR codes to campaign-specific destinations and measurement.',
ctaLabel: 'Create a trackable campaign QR',
},
{
href: featuredUseCases[0].href,
title: featuredUseCases[0].title,
description: featuredUseCases[0].summary,
ctaLabel: featuredUseCases[0].ctaLabel,
},
{
href: featuredUseCases[2].href,
title: featuredUseCases[2].title,
description: featuredUseCases[2].summary,
ctaLabel: featuredUseCases[2].ctaLabel,
},
{
href: '/use-cases',
title: 'Explore the use-case hub',
description: 'Browse QR workflows where design, routing, and measurement need to work together.',
ctaLabel: 'Explore QR code use cases',
},
];
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, faqSchema, breadcrumbSchema(breadcrumbItems)]} />
<MarketingPageTracker pageType="commercial" cluster="custom-qr" />
<div className="min-h-screen bg-white">
{/* Hero Section */}
<section className="relative overflow-hidden bg-gradient-to-br from-purple-50 via-white to-blue-50 py-20">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
@@ -621,11 +652,20 @@ export default function CustomQRCodeGeneratorPage() {
</div>
</div>
</div>
</div>
</section>
{/* Final CTA */}
<section className="py-20 bg-gradient-to-r from-purple-600 to-blue-600 text-white">
</div>
</section>
<GrowthLinksSection
eyebrow="Branded workflows"
title="Where custom QR design supports the workflow"
description="Custom QR codes work best when they support a real business journey, not when they are only decoration. These are the strongest adjacent workflows."
links={relatedUseCaseLinks}
pageType="commercial"
cluster="custom-qr"
/>
{/* Final CTA */}
<section className="py-20 bg-gradient-to-r from-purple-600 to-blue-600 text-white">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-4xl text-center">
<h2 className="text-4xl font-bold mb-6">
Start Creating Branded QR Codes Today

View File

@@ -1,12 +1,14 @@
import React from 'react';
import type { Metadata } from 'next';
import Link from 'next/link';
import { Button } from '@/components/ui/Button';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import SeoJsonLd from '@/components/SeoJsonLd';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { breadcrumbSchema } from '@/lib/schema';
import { AnswerFirstBlock } from '@/components/marketing/AnswerFirstBlock';
import SeoJsonLd from '@/components/SeoJsonLd';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { breadcrumbSchema } from '@/lib/schema';
import { AnswerFirstBlock } from '@/components/marketing/AnswerFirstBlock';
import { GrowthLinksSection } from '@/components/marketing/GrowthLinksSection';
import { MarketingPageTracker, TrackedCtaLink } from '@/components/marketing/MarketingAnalytics';
import { featuredUseCases } from '@/lib/growth-pages';
export const metadata: Metadata = {
title: {
@@ -230,15 +232,43 @@ export default function DynamicQRCodeGeneratorPage() {
],
};
const breadcrumbItems: BreadcrumbItem[] = [
{ name: 'Home', url: '/' },
{ name: 'Dynamic QR Code Generator', url: '/dynamic-qr-code-generator' },
];
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, faqSchema, breadcrumbSchema(breadcrumbItems)]} />
<div className="min-h-screen bg-white">
const breadcrumbItems: BreadcrumbItem[] = [
{ name: 'Home', url: '/' },
{ name: 'Dynamic QR Code Generator', url: '/dynamic-qr-code-generator' },
];
const relatedUseCaseLinks = [
{
href: featuredUseCases[0].href,
title: featuredUseCases[0].title,
description: featuredUseCases[0].summary,
ctaLabel: featuredUseCases[0].ctaLabel,
},
{
href: featuredUseCases[1].href,
title: featuredUseCases[1].title,
description: featuredUseCases[1].summary,
ctaLabel: featuredUseCases[1].ctaLabel,
},
{
href: featuredUseCases[2].href,
title: featuredUseCases[2].title,
description: featuredUseCases[2].summary,
ctaLabel: featuredUseCases[2].ctaLabel,
},
{
href: '/use-cases',
title: 'Explore the use-case hub',
description: 'See how dynamic QR workflows connect to commercial pages, tools, and support content.',
ctaLabel: 'Explore QR code use cases',
},
];
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, faqSchema, breadcrumbSchema(breadcrumbItems)]} />
<MarketingPageTracker pageType="commercial" cluster="dynamic-qr" />
<div className="min-h-screen bg-white">
{/* Hero Section */}
<section className="relative overflow-hidden bg-gradient-to-br from-purple-50 via-white to-blue-50 py-20">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
@@ -274,21 +304,21 @@ export default function DynamicQRCodeGeneratorPage() {
<span className="text-gray-700">{feature}</span>
</div>
))}
</div>
<div className="flex flex-col sm:flex-row gap-4">
<Link href="/signup">
<Button size="lg" className="text-lg px-8 py-4 w-full sm:w-auto">
Create Dynamic QR Code
</Button>
</Link>
<Link href="/pricing">
<Button variant="outline" size="lg" className="text-lg px-8 py-4 w-full sm:w-auto">
View Pricing
</Button>
</Link>
</div>
</div>
</div>
<div className="flex flex-col sm:flex-row gap-4">
<TrackedCtaLink href="/signup" ctaLabel="Create Dynamic QR Code" ctaLocation="hero_primary" pageType="commercial" cluster="dynamic-qr">
<Button size="lg" className="text-lg px-8 py-4 w-full sm:w-auto">
Create Dynamic QR Code
</Button>
</TrackedCtaLink>
<TrackedCtaLink href="/pricing" ctaLabel="View Pricing" ctaLocation="hero_secondary" pageType="commercial" cluster="dynamic-qr">
<Button variant="outline" size="lg" className="text-lg px-8 py-4 w-full sm:w-auto">
View Pricing
</Button>
</TrackedCtaLink>
</div>
</div>
{/* Visual Demo */}
<div className="relative">
@@ -514,32 +544,41 @@ export default function DynamicQRCodeGeneratorPage() {
</p>
</Card>
</div>
</div>
</section>
{/* CTA Section */}
<section className="py-20 bg-gradient-to-r from-purple-600 to-blue-600 text-white">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-4xl text-center">
<h2 className="text-4xl font-bold mb-6">
Start Creating Dynamic QR Codes Today
</h2>
<p className="text-xl mb-8 text-purple-100">
Join thousands of businesses who never worry about reprinting QR codes again
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link href="/signup">
<Button size="lg" variant="secondary" className="text-lg px-8 py-4 w-full sm:w-auto bg-white text-purple-600 hover:bg-gray-100">
Get Started Free
</Button>
</Link>
<Link href="/signup">
<Button size="lg" variant="outline" className="text-lg px-8 py-4 w-full sm:w-auto border-white text-white hover:bg-white/10">
Create QR Code Now
</Button>
</Link>
</div>
</div>
</section>
</div>
</section>
<GrowthLinksSection
eyebrow="Best next workflows"
title="See where dynamic QR becomes most useful"
description="These are the strongest first workflows for dynamic QR because the printed asset stays the same while the destination or campaign context keeps moving."
links={relatedUseCaseLinks}
pageType="commercial"
cluster="dynamic-qr"
/>
{/* CTA Section */}
<section className="py-20 bg-gradient-to-r from-purple-600 to-blue-600 text-white">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-4xl text-center">
<h2 className="text-4xl font-bold mb-6">
Start Creating Dynamic QR Codes Today
</h2>
<p className="text-xl mb-8 text-purple-100">
Use one QR code that can keep working even when the destination behind it needs to change.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<TrackedCtaLink href="/signup" ctaLabel="Get Started Free" ctaLocation="footer_primary" pageType="commercial" cluster="dynamic-qr">
<Button size="lg" variant="secondary" className="text-lg px-8 py-4 w-full sm:w-auto bg-white text-purple-600 hover:bg-gray-100">
Get Started Free
</Button>
</TrackedCtaLink>
<TrackedCtaLink href="/signup" ctaLabel="Create QR Code Now" ctaLocation="footer_secondary" pageType="commercial" cluster="dynamic-qr">
<Button size="lg" variant="outline" className="text-lg px-8 py-4 w-full sm:w-auto border-white text-white hover:bg-white/10">
Create QR Code Now
</Button>
</TrackedCtaLink>
</div>
</div>
</section>
</div>
</>
);

View File

@@ -1,11 +1,8 @@
import type { Metadata } from 'next';
import AdBanner from '@/components/ads/AdBanner';
import '@/styles/globals.css';
import { Providers } from '@/components/Providers';
import MarketingLayout from './MarketingLayout';
// Import schema functions from library
import { organizationSchema, websiteSchema } from '@/lib/schema';
import FacebookPixel from '@/components/analytics/FacebookPixel';
import type { Metadata } from 'next';
import '@/styles/globals.css';
import MarketingLayout from './MarketingLayout';
// Import schema functions from library
import { organizationSchema } from '@/lib/schema';
const isIndexable = process.env.NEXT_PUBLIC_INDEXABLE === 'true';
@@ -54,29 +51,20 @@ export const metadata: Metadata = {
},
};
export default function MarketingGroupLayout({
children,
}: {
children: React.ReactNode;
}) {
export default function MarketingGroupLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }}
/>
<MarketingLayout>
{children}
</MarketingLayout>
{/* Global Marketing Ad - Exclusions handled in AdBanner */}
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl pb-8">
<AdBanner
dataAdSlot="2607110637"
dataAdFormat="auto"
fullWidthResponsive={true}
className="bg-slate-50 rounded-xl p-4 border border-slate-100"
/>
</div>
</>
);
}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }}
/>
<MarketingLayout>
{children}
</MarketingLayout>
</>
);
}

View File

@@ -8,14 +8,20 @@ import { pillarPageSchema, faqPageSchema } from "@/lib/schema";
import { FAQSection } from "@/components/aeo/FAQSection";
import { AnswerBox } from "@/components/aeo/AnswerBox";
export function generateMetadata({ params }: { params: { pillar: string } }) {
const meta = pillarMeta.find(p => p.key === params.pillar);
if (!meta) return {};
return {
title: `${meta.title} - Ultimate Guide | QR Master`,
description: meta.description
};
}
export function generateMetadata({ params }: { params: { pillar: string } }) {
const meta = pillarMeta.find(p => p.key === params.pillar);
if (!meta) return {};
return {
title: `${meta.title} - Ultimate Guide | QR Master`,
description: meta.description,
alternates: {
canonical: `https://www.qrmaster.net/learn/${meta.key}`,
},
openGraph: {
url: `https://www.qrmaster.net/learn/${meta.key}`,
},
};
}
export function generateStaticParams() {
return pillarMeta.map((pillar) => ({

View File

@@ -2,10 +2,16 @@ import Link from "next/link";
import { pillarMeta } from "@/lib/pillar-data";
import { getPublishedPosts } from "@/lib/content";
export const metadata = {
title: "Learn QR Code Mastery | QR Master Hub",
description: "Guides, use cases, tracking deep-dives, and security best practices for dynamic QR codes.",
};
export const metadata = {
title: "Learn QR Code Mastery | QR Master Hub",
description: "Guides, use cases, tracking deep-dives, and security best practices for dynamic QR codes.",
alternates: {
canonical: "https://www.qrmaster.net/learn",
},
openGraph: {
url: "https://www.qrmaster.net/learn",
},
};
export default function LearnHubPage() {
const posts = getPublishedPosts();

View File

@@ -0,0 +1,112 @@
import type { Metadata } from "next";
import {
buildUseCaseMetadata,
UseCasePageTemplate,
} from "@/components/marketing/UseCasePageTemplate";
export const metadata: Metadata = buildUseCaseMetadata({
title: "QR Codes for Marketing Campaigns",
description:
"Plan QR codes for marketing campaigns around placement tracking, changing destinations, and offline-to-online attribution.",
canonicalPath: "/qr-code-for-marketing-campaigns",
});
export default function QRCodeForMarketingCampaignsPage() {
return (
<UseCasePageTemplate
title="QR Codes for Marketing Campaigns"
description="Plan QR codes for marketing campaigns around placement tracking, changing destinations, and offline-to-online attribution."
eyebrow="Campaign Workflows"
intro="Marketing campaign QR codes work best when the code on the printed asset stays stable while the destination and attribution model can evolve with the campaign."
pageType="commercial"
cluster="marketing-campaigns"
useCase="marketing-campaigns"
breadcrumbs={[
{ name: "Home", url: "/" },
{
name: "QR Codes for Marketing Campaigns",
url: "/qr-code-for-marketing-campaigns",
},
]}
answer="A campaign QR code should do more than open a page. It should help you compare placements, update the destination when the offer changes, and route offline traffic into a measurable funnel."
whenToUse={[
"You run flyers, posters, packaging inserts, or event signage with campaign-specific CTA copy.",
"You want to compare placements or creatives instead of treating every scan as generic traffic.",
"Your destination may change during the life of the printed campaign.",
]}
comparisonItems={[
{ label: "Offer updates", text: "New print required", value: true },
{ label: "Placement attribution", text: "Often manual", value: true },
{ label: "Creative testing", text: "Hard to manage", value: true },
]}
howToSteps={[
"Create campaign QR flows around one clear action and one named placement context.",
"Use dynamic destinations or tagged URLs so the print stays usable when the offer changes.",
"Measure scans with a clean CTA path into signup, lead capture, or campaign landing pages.",
]}
primaryCta={{
href: "/dynamic-qr-code-generator",
label: "Create a trackable campaign QR",
}}
secondaryCta={{
href: "/use-cases",
label: "Browse use-case workflows",
}}
workflowTitle="What strong campaign QR workflows look like"
workflowIntro="Campaign QR strategy becomes more useful when creative, placement, and destination are treated as a system rather than a single link printed everywhere."
workflowCards={[
{
title: "Placement-aware routing",
description: "Keep banner, flyer, packaging, and in-store placements comparable by using distinct destinations or campaign tags.",
},
{
title: "Post-print flexibility",
description: "Adjust the landing page, offer, or CTA destination after print when the campaign learns something or needs a fast update.",
},
{
title: "Measurement-ready handoff",
description: "Push campaign scans toward signup, booking, or lead-gen paths so the QR is tied to a business outcome instead of a vanity click.",
},
]}
checklistTitle="Campaign QR checklist"
checklist={[
"Match each QR code to one campaign purpose and one primary CTA.",
"Differentiate placements with clean naming or URL tagging before the assets go to print.",
"Use a destination you can update when the promotion, offer, or landing page changes.",
"Link the campaign flow back to a measured CTA path instead of stopping at raw scan counts.",
]}
supportLinks={[
{
href: "/qr-code-tracking",
title: "QR Code Tracking",
description: "Use when the real priority is measuring placement and scanner context.",
},
{
href: "/custom-qr-code-generator",
title: "Custom QR Code Generator",
description: "Useful when brand fit and print creative need more control.",
},
{
href: "/blog/utm-parameter-qr-codes",
title: "UTM Parameters with QR Codes",
description: "Support article for placement naming and campaign attribution strategy.",
},
]}
faq={[
{
question: "Why use QR codes in marketing campaigns?",
answer: "Campaign QR codes help move offline audiences into a measurable online path. They are most useful when the destination and tracking setup are planned before the assets go live.",
},
{
question: "Should campaign QR codes be dynamic?",
answer: "Yes, when the destination, offer, or campaign landing page may change after print. That avoids replacing materials just because the target page changes.",
},
{
question: "How do I track different QR placements in one campaign?",
answer: "Use distinct destinations or tagged URLs for each placement so flyers, posters, booth signs, and packaging inserts can be compared cleanly.",
},
]}
/>
);
}

View File

@@ -1,11 +1,13 @@
import React from 'react';
import type { Metadata } from 'next';
import Link from 'next/link';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import SeoJsonLd from '@/components/SeoJsonLd';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { breadcrumbSchema } from '@/lib/schema';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import SeoJsonLd from '@/components/SeoJsonLd';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { breadcrumbSchema } from '@/lib/schema';
import { GrowthLinksSection } from '@/components/marketing/GrowthLinksSection';
import { MarketingPageTracker, TrackedCtaLink } from '@/components/marketing/MarketingAnalytics';
import { featuredUseCases } from '@/lib/growth-pages';
export const metadata: Metadata = {
title: {
@@ -164,15 +166,43 @@ export default function QRCodeTrackingPage() {
],
};
const breadcrumbItems: BreadcrumbItem[] = [
{ name: 'Home', url: '/' },
{ name: 'QR Code Tracking', url: '/qr-code-tracking' },
];
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, breadcrumbSchema(breadcrumbItems)]} />
<div className="min-h-screen bg-white">
const breadcrumbItems: BreadcrumbItem[] = [
{ name: 'Home', url: '/' },
{ name: 'QR Code Tracking', url: '/qr-code-tracking' },
];
const relatedUseCaseLinks = [
{
href: featuredUseCases[2].href,
title: featuredUseCases[2].title,
description: featuredUseCases[2].summary,
ctaLabel: featuredUseCases[2].ctaLabel,
},
{
href: '/qr-code-for-marketing-campaigns',
title: 'QR Codes for Marketing Campaigns',
description: 'Map scans to campaign placements, creative tests, and offline-to-online attribution.',
ctaLabel: 'Create a trackable campaign QR',
},
{
href: featuredUseCases[0].href,
title: featuredUseCases[0].title,
description: featuredUseCases[0].summary,
ctaLabel: featuredUseCases[0].ctaLabel,
},
{
href: '/use-cases',
title: 'Explore the use-case hub',
description: 'Browse the first commercial workflows built around dynamic updates and measurable scans.',
ctaLabel: 'Explore QR code use cases',
},
];
return (
<>
<SeoJsonLd data={[softwareSchema, howToSchema, breadcrumbSchema(breadcrumbItems)]} />
<MarketingPageTracker pageType="commercial" cluster="qr-tracking" />
<div className="min-h-screen bg-white">
{/* Hero Section */}
<section className="relative overflow-hidden bg-gradient-to-br from-blue-50 via-white to-purple-50 py-20">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
@@ -190,20 +220,20 @@ export default function QRCodeTrackingPage() {
<p className="text-xl text-gray-600 leading-relaxed">
Monitor your QR code performance in real-time. Get detailed insights on location, device, time, and user behavior. Make data-driven decisions with our free tracking software.
</p>
<div className="flex flex-col sm:flex-row gap-4">
<Link href="/signup">
<Button size="lg" className="text-lg px-8 py-4 w-full sm:w-auto">
Start Tracking Free
</Button>
</Link>
<Link href="/signup">
<Button variant="outline" size="lg" className="text-lg px-8 py-4 w-full sm:w-auto">
Create Trackable QR Code
</Button>
</Link>
</div>
</p>
<div className="flex flex-col sm:flex-row gap-4">
<TrackedCtaLink href="/signup" ctaLabel="Start Tracking Free" ctaLocation="hero_primary" pageType="commercial" cluster="qr-tracking">
<Button size="lg" className="text-lg px-8 py-4 w-full sm:w-auto">
Start Tracking Free
</Button>
</TrackedCtaLink>
<TrackedCtaLink href="/signup" ctaLabel="Create Trackable QR Code" ctaLocation="hero_secondary" pageType="commercial" cluster="qr-tracking">
<Button variant="outline" size="lg" className="text-lg px-8 py-4 w-full sm:w-auto">
Create Trackable QR Code
</Button>
</TrackedCtaLink>
</div>
<div className="flex items-center space-x-6 text-sm text-gray-600">
<div className="flex items-center space-x-2">
@@ -212,13 +242,13 @@ export default function QRCodeTrackingPage() {
</svg>
<span>No credit card required</span>
</div>
<div className="flex items-center space-x-2">
<svg className="w-5 h-5 text-green-500" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
<span>Unlimited scans</span>
</div>
</div>
<div className="flex items-center space-x-2">
<svg className="w-5 h-5 text-green-500" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
<span>Placement-ready reports</span>
</div>
</div>
</div>
{/* Analytics Preview */}
@@ -227,20 +257,20 @@ export default function QRCodeTrackingPage() {
<h3 className="font-semibold text-lg mb-4">Live Analytics Dashboard</h3>
<div className="space-y-4">
<div className="flex justify-between items-center pb-3 border-b">
<span className="text-gray-600">Total Scans</span>
<span className="text-2xl font-bold text-primary-600">12,547</span>
<span className="text-gray-600">Placement view</span>
<span className="text-base font-semibold text-primary-600">Flyer vs booth vs table card</span>
</div>
<div className="flex justify-between items-center pb-3 border-b">
<span className="text-gray-600">Unique Users</span>
<span className="text-2xl font-bold text-primary-600">8,392</span>
<span className="text-gray-600">Time trend</span>
<span className="text-base font-semibold text-primary-600">Lunch, event day, or campaign burst</span>
</div>
<div className="flex justify-between items-center pb-3 border-b">
<span className="text-gray-600">Top Location</span>
<span className="font-semibold">🇩🇪 Germany</span>
<span className="text-gray-600">Location context</span>
<span className="font-semibold">Region and city level view</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-600">Top Device</span>
<span className="font-semibold">📱 iPhone</span>
<span className="text-gray-600">Device context</span>
<span className="font-semibold">Phone, desktop, and scan mix</span>
</div>
</div>
</Card>
@@ -318,9 +348,9 @@ export default function QRCodeTrackingPage() {
</section>
{/* Comparison Table */}
<section className="py-20 bg-gray-50">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl">
<div className="text-center mb-16">
<section className="py-20 bg-gray-50">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl">
<div className="text-center mb-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4">
QR Master vs Free Tools
</h2>
@@ -364,33 +394,42 @@ export default function QRCodeTrackingPage() {
))}
</tbody>
</table>
</Card>
</div>
</section>
{/* CTA Section */}
<section className="py-20 bg-gradient-to-r from-primary-600 to-purple-600 text-white">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-4xl text-center">
<h2 className="text-4xl font-bold mb-6">
Start Tracking Your QR Codes Today
</h2>
<p className="text-xl mb-8 text-primary-100">
Join thousands of businesses using QR Master to track and optimize their QR code campaigns
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link href="/signup">
<Button size="lg" variant="secondary" className="text-lg px-8 py-4 w-full sm:w-auto bg-white text-primary-600 hover:bg-gray-100">
Create Free Account
</Button>
</Link>
<Link href="/pricing">
<Button size="lg" variant="outline" className="text-lg px-8 py-4 w-full sm:w-auto border-white text-white hover:bg-white/10">
View Pricing
</Button>
</Link>
</div>
</div>
</section>
</Card>
</div>
</section>
<GrowthLinksSection
eyebrow="Tracking-led workflows"
title="Where scan visibility matters most"
description="These are the workflows where scan context, placement comparison, and destination flexibility make QR tracking materially more useful."
links={relatedUseCaseLinks}
pageType="commercial"
cluster="qr-tracking"
/>
{/* CTA Section */}
<section className="py-20 bg-gradient-to-r from-primary-600 to-purple-600 text-white">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-4xl text-center">
<h2 className="text-4xl font-bold mb-6">
Start Tracking Your QR Codes Today
</h2>
<p className="text-xl mb-8 text-primary-100">
Measure scans with enough context to improve the next placement, campaign, or printed workflow.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<TrackedCtaLink href="/signup" ctaLabel="Create Free Account" ctaLocation="footer_primary" pageType="commercial" cluster="qr-tracking">
<Button size="lg" variant="secondary" className="text-lg px-8 py-4 w-full sm:w-auto bg-white text-primary-600 hover:bg-gray-100">
Create Free Account
</Button>
</TrackedCtaLink>
<TrackedCtaLink href="/pricing" ctaLabel="View Pricing" ctaLocation="footer_secondary" pageType="commercial" cluster="qr-tracking">
<Button size="lg" variant="outline" className="text-lg px-8 py-4 w-full sm:w-auto border-white text-white hover:bg-white/10">
View Pricing
</Button>
</TrackedCtaLink>
</div>
</div>
</section>
</div>
</>
);

View File

@@ -0,0 +1,78 @@
import { notFound } from "next/navigation";
import {
buildUseCaseMetadata,
UseCasePageTemplate,
} from "@/components/marketing/UseCasePageTemplate";
import {
featuredUseCases,
getUseCasePage,
} from "@/lib/growth-pages";
export function generateStaticParams() {
return featuredUseCases.map((item) => ({
slug: item.slug,
}));
}
export function generateMetadata({ params }: { params: { slug: string } }) {
const page = getUseCasePage(params.slug);
if (!page) {
return {};
}
return buildUseCaseMetadata({
title: page.title,
description: page.metaDescription,
canonicalPath: page.href,
});
}
export default function UseCaseDetailPage({
params,
}: {
params: { slug: string };
}) {
const page = getUseCasePage(params.slug);
if (!page) {
notFound();
}
return (
<UseCasePageTemplate
title={page.title}
description={page.metaDescription}
eyebrow={page.eyebrow}
intro={page.intro}
pageType="use_case"
cluster={page.cluster}
useCase={page.slug}
breadcrumbs={[
{ name: "Home", url: "/" },
{ name: "Use Cases", url: "/use-cases" },
{ name: page.title, url: page.href },
]}
answer={page.answer}
whenToUse={page.whenToUse}
comparisonItems={page.comparisonItems}
howToSteps={page.howToSteps}
primaryCta={{
href: page.parentHref,
label: page.ctaLabel,
}}
secondaryCta={{
href: "/use-cases",
label: "Explore more use cases",
}}
workflowTitle={page.workflowTitle}
workflowIntro={page.workflowIntro}
workflowCards={page.workflowCards}
checklistTitle={page.checklistTitle}
checklist={page.checklist}
supportLinks={page.supportLinks}
faq={page.faq}
/>
);
}

View File

@@ -0,0 +1,303 @@
import type { Metadata } from "next";
import Link from "next/link";
import {
ArrowRight,
Compass,
LibraryBig,
Link2,
Route,
Sparkles,
} from "lucide-react";
import Breadcrumbs, { BreadcrumbItem } from "@/components/Breadcrumbs";
import SeoJsonLd from "@/components/SeoJsonLd";
import {
MarketingPageTracker,
TrackedCtaLink,
} from "@/components/marketing/MarketingAnalytics";
import { Button } from "@/components/ui/Button";
import { Card } from "@/components/ui/Card";
import {
commercialPages,
featuredUseCases,
supportResources,
upcomingUseCaseIdeas,
} from "@/lib/growth-pages";
import { breadcrumbSchema } from "@/lib/schema";
export const metadata: Metadata = {
title: {
absolute: "QR Code Use Cases for Business | QR Master",
},
description:
"Explore QR code use cases for restaurants, events, business cards, and campaign workflows built around dynamic updates and tracking.",
alternates: {
canonical: "https://www.qrmaster.net/use-cases",
languages: {
"x-default": "https://www.qrmaster.net/use-cases",
en: "https://www.qrmaster.net/use-cases",
},
},
openGraph: {
title: "QR Code Use Cases for Business | QR Master",
description:
"Explore QR code use cases for restaurants, events, business cards, and campaign workflows built around dynamic updates and tracking.",
url: "https://www.qrmaster.net/use-cases",
type: "website",
images: ["/og-image.png"],
},
twitter: {
title: "QR Code Use Cases for Business | QR Master",
description:
"Explore QR code use cases for restaurants, events, business cards, and campaign workflows built around dynamic updates and tracking.",
},
};
export default function UseCasesHubPage() {
const breadcrumbItems: BreadcrumbItem[] = [
{ name: "Home", url: "/" },
{ name: "Use Cases", url: "/use-cases" },
];
return (
<>
<SeoJsonLd data={[breadcrumbSchema(breadcrumbItems)]} />
<MarketingPageTracker pageType="use_case_hub" cluster="all-use-cases" />
<div className="min-h-screen bg-white">
<section className="relative overflow-hidden bg-gradient-to-br from-slate-950 via-blue-950 to-cyan-950 text-white">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(125,211,252,0.18),transparent_34%),radial-gradient(circle_at_right,rgba(255,255,255,0.06),transparent_28%)]" />
<div className="relative container mx-auto max-w-7xl px-4 py-20 sm:px-6 lg:px-8">
<Breadcrumbs
items={breadcrumbItems}
className="[&_a]:text-blue-100/80 [&_a:hover]:text-white [&_span]:text-blue-100/80 [&_[aria-current=page]]:text-white"
/>
<div className="grid gap-12 lg:grid-cols-[minmax(0,1.2fr)_minmax(320px,0.8fr)] lg:items-center">
<div className="space-y-8">
<div className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/10 px-4 py-2 text-sm font-semibold text-cyan-100 shadow-lg shadow-cyan-950/30 backdrop-blur">
<Sparkles className="h-4 w-4" />
<span>Commercial use-case hub</span>
</div>
<div className="space-y-5">
<h1 className="max-w-4xl text-4xl font-bold tracking-tight text-white md:text-5xl lg:text-6xl">
QR code use cases that fit real business workflows
</h1>
<p className="max-w-3xl text-lg leading-8 text-blue-50/88 md:text-xl">
This hub focuses on workflows where dynamic updates and
measurement matter. It is not a list of random QR ideas. It
is the commercial layer between QR Master's product pages,
tools, and editorial content.
</p>
</div>
<div className="grid gap-3 text-sm text-blue-50/80 sm:grid-cols-2">
{[
"Use-case pages map back to a clear commercial parent.",
"Each workflow is written for practical deployment, not filler traffic.",
"Support resources reinforce the wedge around dynamic and trackable QR flows.",
"The next cluster expansion will build on measurable routing and internal links.",
].map((line) => (
<div
key={line}
className="flex items-start gap-3 rounded-2xl border border-white/10 bg-white/5 px-4 py-3 backdrop-blur-sm"
>
<Route className="mt-0.5 h-4 w-4 shrink-0 text-cyan-300" />
<span>{line}</span>
</div>
))}
</div>
<div className="flex flex-col gap-4 sm:flex-row">
<TrackedCtaLink
href={featuredUseCases[0].href}
ctaLabel="Explore restaurant menu QR codes"
ctaLocation="hero_primary"
pageType="use_case_hub"
cluster="all-use-cases"
>
<Button size="lg" className="w-full bg-white px-8 py-4 text-slate-950 hover:bg-slate-100 sm:w-auto">
Explore featured workflows
</Button>
</TrackedCtaLink>
<TrackedCtaLink
href="/qr-code-for-marketing-campaigns"
ctaLabel="View marketing campaign QR page"
ctaLocation="hero_secondary"
pageType="use_case_hub"
cluster="all-use-cases"
>
<Button
variant="outline"
size="lg"
className="w-full border-white/30 bg-white/5 px-8 py-4 text-white hover:bg-white/10 sm:w-auto"
>
See campaign workflows
</Button>
</TrackedCtaLink>
</div>
</div>
<Card className="border-white/10 bg-white/10 p-8 text-white shadow-2xl shadow-slate-950/30 backdrop-blur">
<div className="space-y-5">
<div className="flex items-center gap-3">
<Compass className="h-5 w-5 text-cyan-300" />
<h2 className="text-2xl font-bold">How to use this hub</h2>
</div>
<div className="space-y-4 text-sm leading-6 text-blue-50/82">
<p>
Start with the workflow problem, not the QR format. If the
printed code needs to survive destination changes or you
need proof of performance, begin with the use case that
matches that job.
</p>
<p>
Each page below links back to the best product parent,
forward to related workflows, and sideways to educational
resources that help you deploy the QR well.
</p>
</div>
</div>
</Card>
</div>
</div>
</section>
<section className="py-16">
<div className="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mb-10 max-w-3xl">
<div className="text-sm font-semibold uppercase tracking-[0.22em] text-blue-700">
Featured use cases
</div>
<h2 className="mt-3 text-3xl font-bold text-slate-900">
First workflows in the growth rollout
</h2>
<p className="mt-4 text-lg leading-8 text-slate-600">
These are the first routes worth surfacing because they connect
cleanly to QR Master's strongest product angles and existing
supporting content.
</p>
</div>
<div className="grid gap-6 lg:grid-cols-3">
{featuredUseCases.map((page) => (
<Link key={page.slug} href={page.href} className="group block">
<Card className="flex h-full flex-col rounded-3xl border-slate-200 bg-white p-7 shadow-sm transition-all hover:-translate-y-1 hover:shadow-lg">
<div className="text-sm font-semibold uppercase tracking-[0.18em] text-blue-700">
{page.cluster}
</div>
<h3 className="mt-4 text-2xl font-bold text-slate-900">
{page.title}
</h3>
<p className="mt-4 flex-1 text-base leading-7 text-slate-600">
{page.summary}
</p>
<div className="mt-6 flex items-center justify-between rounded-2xl bg-slate-50 px-4 py-3 text-sm text-slate-600">
<span>Primary parent: {page.parentTitle}</span>
<ArrowRight className="h-4 w-4 text-blue-700 transition-transform group-hover:translate-x-1" />
</div>
</Card>
</Link>
))}
</div>
</div>
</section>
<section className="bg-slate-50 py-16">
<div className="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="grid gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(0,0.95fr)]">
<Card className="rounded-3xl border-slate-200 bg-white p-8 shadow-sm">
<div className="flex items-center gap-3">
<LibraryBig className="h-5 w-5 text-blue-700" />
<h2 className="text-2xl font-bold text-slate-900">
Commercial pages that anchor the hub
</h2>
</div>
<div className="mt-6 grid gap-4 md:grid-cols-2">
{commercialPages.map((page) => (
<Link
key={page.href}
href={page.href}
className="rounded-2xl border border-slate-200 p-4 transition-colors hover:border-blue-200 hover:bg-blue-50/60"
>
<div className={`h-1.5 rounded-full bg-gradient-to-r ${page.accent}`} />
<div className="mt-4 text-lg font-semibold text-slate-900">
{page.title}
</div>
<p className="mt-2 text-sm leading-6 text-slate-600">
{page.description}
</p>
</Link>
))}
</div>
</Card>
<Card className="rounded-3xl border-slate-200 bg-slate-950 p-8 text-white shadow-xl shadow-slate-200">
<div className="flex items-center gap-3">
<Link2 className="h-5 w-5 text-cyan-300" />
<h2 className="text-2xl font-bold">Support resources</h2>
</div>
<div className="mt-6 space-y-4">
{supportResources.map((resource) => (
<Link
key={resource.href}
href={resource.href}
className="block rounded-2xl border border-white/10 bg-white/5 p-4 transition-colors hover:bg-white/10"
>
<div className="text-lg font-semibold text-white">
{resource.title}
</div>
<p className="mt-2 text-sm leading-6 text-blue-50/78">
{resource.description}
</p>
</Link>
))}
</div>
</Card>
</div>
</div>
</section>
<section className="py-16">
<div className="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mb-10 max-w-3xl">
<div className="text-sm font-semibold uppercase tracking-[0.22em] text-blue-700">
Next cluster candidates
</div>
<h2 className="mt-3 text-3xl font-bold text-slate-900">
What follows after the first use-case wave
</h2>
<p className="mt-4 text-lg leading-8 text-slate-600">
These are not published use-case routes yet. They are the next
practical cluster expansions once the first hub and CTA layer are
established.
</p>
</div>
<div className="grid gap-6 md:grid-cols-3">
{upcomingUseCaseIdeas.map((item) => (
<Card
key={item.title}
className="rounded-3xl border-dashed border-slate-300 bg-slate-50 p-7"
>
<div className="text-xl font-semibold text-slate-900">
{item.title}
</div>
<p className="mt-3 text-base leading-7 text-slate-600">
{item.description}
</p>
<div className="mt-5 text-sm font-semibold text-blue-700">
Anchored by {item.href.replace("/", "")}
</div>
</Card>
))}
</div>
</div>
</section>
</div>
</>
);
}

View File

@@ -1,11 +1,10 @@
import type { Metadata } from 'next';
import { Suspense } from 'react';
import '@/styles/globals.css';
import { Providers } from '@/components/Providers';
import MarketingDeLayout from './MarketingDeLayout';
import { organizationSchema, websiteSchema } from '@/lib/schema';
import AdSenseScript from '@/components/ads/AdSenseScript';
import FacebookPixel from '@/components/analytics/FacebookPixel';
import { Suspense } from 'react';
import '@/styles/globals.css';
import { Providers } from '@/components/Providers';
import MarketingDeLayout from './MarketingDeLayout';
import { organizationSchema, websiteSchema } from '@/lib/schema';
import FacebookPixel from '@/components/analytics/FacebookPixel';
export const metadata: Metadata = {
title: {
@@ -55,46 +54,33 @@ export const metadata: Metadata = {
'facebook-domain-verification': process.env.NEXT_PUBLIC_FACEBOOK_DOMAIN_VERIFICATION || '',
},
},
};
import AdBanner from '@/components/ads/AdBanner'; // Import AdBanner
export default function MarketingDeGroupLayout({
children,
}: {
};
export default function MarketingDeGroupLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="de">
<body className="font-sans">
<Suspense fallback={null}>
<Providers>
<AdSenseScript />
<FacebookPixel />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }}
<body className="font-sans">
<Suspense fallback={null}>
<Providers>
<FacebookPixel />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteSchema()) }}
/>
<MarketingDeLayout>
{children}
{/* Global Marketing Ad - Exclusions handled in AdBanner */}
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl pb-8">
<AdBanner
dataAdSlot="2607110637"
dataAdFormat="auto"
fullWidthResponsive={true}
className="bg-slate-50 rounded-xl p-4 border border-slate-100"
/>
</div>
</MarketingDeLayout>
</Providers>
</Suspense>
</body>
/>
<MarketingDeLayout>
{children}
</MarketingDeLayout>
</Providers>
</Suspense>
</body>
</html>
);
}

View File

@@ -53,20 +53,53 @@ export default function sitemap(): MetadataRoute.Sitemap {
}));
// Learn hub and pillar pages
const learnPages = [
{
url: `${baseUrl}/learn`,
const learnPages = [
{
url: `${baseUrl}/learn`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: 0.9,
},
...pillarMeta.map((pillar) => ({
url: `${baseUrl}/learn/${pillar.key}`,
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.8,
})),
];
...pillarMeta.map((pillar) => ({
url: `${baseUrl}/learn/${pillar.key}`,
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.8,
})),
];
const growthUseCasePages = [
{
url: `${baseUrl}/use-cases`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: 0.9,
},
{
url: `${baseUrl}/use-cases/restaurant-menu-qr-codes`,
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.8,
},
{
url: `${baseUrl}/use-cases/business-card-qr-codes`,
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.8,
},
{
url: `${baseUrl}/use-cases/event-qr-codes`,
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.8,
},
{
url: `${baseUrl}/qr-code-for-marketing-campaigns`,
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.85,
},
];
// Author pages
const authorPages = authors.map((author) => ({
@@ -189,10 +222,11 @@ export default function sitemap(): MetadataRoute.Sitemap {
},
...toolPages,
...blogPages,
...learnPages,
...authorPages,
];
}
...toolPages,
...blogPages,
...learnPages,
...growthUseCasePages,
...authorPages,
];
}

View File

@@ -1,21 +1,20 @@
'use client';
import { Suspense } from 'react';
import { ToastContainer } from '@/components/ui/Toast';
import AuthProvider from '@/components/SessionProvider';
import { PostHogProvider, PostHogPageView } from '@/components/PostHogProvider';
import CookieBanner from '@/components/CookieBanner';
import GoogleAnalytics from '@/components/analytics/GoogleAnalytics';
import FacebookPixel from '@/components/analytics/FacebookPixel';
import { Suspense } from 'react';
import { ToastContainer } from '@/components/ui/Toast';
import AuthProvider from '@/components/SessionProvider';
import { PostHogProvider } from '@/components/PostHogProvider';
import CookieBanner from '@/components/CookieBanner';
import GoogleAnalytics from '@/components/analytics/GoogleAnalytics';
import FacebookPixel from '@/components/analytics/FacebookPixel';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<PostHogProvider>
<Suspense fallback={null}>
<PostHogPageView />
<GoogleAnalytics />
<FacebookPixel />
</Suspense>
return (
<PostHogProvider>
<Suspense fallback={null}>
<GoogleAnalytics />
<FacebookPixel />
</Suspense>
<AuthProvider>
{children}
</AuthProvider>

View File

@@ -0,0 +1,73 @@
import { ArrowRight } from "lucide-react";
import { TrackedCtaLink } from "@/components/marketing/MarketingAnalytics";
import { Card } from "@/components/ui/Card";
type PageType = "commercial" | "use_case_hub" | "use_case";
type GrowthLink = {
href: string;
title: string;
description: string;
ctaLabel: string;
};
export function GrowthLinksSection({
eyebrow,
title,
description,
links,
pageType,
cluster,
useCase,
}: {
eyebrow: string;
title: string;
description: string;
links: GrowthLink[];
pageType: PageType;
cluster: string;
useCase?: string;
}) {
return (
<section className="py-20 bg-slate-50">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl">
<div className="max-w-3xl mb-12">
<div className="text-sm font-semibold uppercase tracking-[0.22em] text-blue-700">
{eyebrow}
</div>
<h2 className="mt-3 text-4xl font-bold text-slate-900">{title}</h2>
<p className="mt-4 text-xl text-slate-600">{description}</p>
</div>
<div className="grid gap-6 lg:grid-cols-4">
{links.map((link) => (
<TrackedCtaLink
key={link.href}
href={link.href}
ctaLabel={link.ctaLabel}
ctaLocation="related_workflows"
pageType={pageType}
cluster={cluster}
useCase={useCase}
className="group block h-full"
>
<Card className="h-full rounded-3xl border-slate-200 bg-white p-7 shadow-sm transition-all hover:-translate-y-1 hover:shadow-lg">
<div className="text-lg font-semibold text-slate-900">
{link.title}
</div>
<p className="mt-3 text-base leading-7 text-slate-600">
{link.description}
</p>
<div className="mt-6 flex items-center gap-2 text-sm font-semibold text-blue-700">
<span>Open workflow</span>
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-1" />
</div>
</Card>
</TrackedCtaLink>
))}
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,94 @@
"use client";
import Link from "next/link";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";
import { trackEvent } from "@/components/PostHogProvider";
type PageType = "commercial" | "use_case_hub" | "use_case";
type TrackingContext = {
pageType: PageType;
cluster?: string;
useCase?: string;
};
function getUtmProperties(searchParams: ReturnType<typeof useSearchParams>) {
return {
utm_source: searchParams?.get("utm_source") || undefined,
utm_medium: searchParams?.get("utm_medium") || undefined,
utm_campaign: searchParams?.get("utm_campaign") || undefined,
utm_content: searchParams?.get("utm_content") || undefined,
};
}
export function MarketingPageTracker({
pageType,
cluster,
useCase,
}: TrackingContext) {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
if (!pathname) {
return;
}
trackEvent("landing_page_viewed", {
landing_page_slug: pathname,
page_type: pageType,
cluster,
use_case: useCase,
...getUtmProperties(searchParams),
});
}, [cluster, pageType, pathname, searchParams, useCase]);
return null;
}
type TrackedCtaLinkProps = TrackingContext & {
href: string;
ctaLabel: string;
ctaLocation: string;
destination?: string;
className?: string;
children: React.ReactNode;
};
export function TrackedCtaLink({
href,
ctaLabel,
ctaLocation,
destination,
className,
children,
pageType,
cluster,
useCase,
}: TrackedCtaLinkProps) {
const pathname = usePathname();
const searchParams = useSearchParams();
return (
<Link
href={href}
className={className}
onClick={() => {
trackEvent("cta_clicked", {
landing_page_slug: pathname,
page_type: pageType,
cluster,
use_case: useCase,
cta_label: ctaLabel,
cta_location: ctaLocation,
destination: destination || href,
...getUtmProperties(searchParams),
});
}}
>
{children}
</Link>
);
}

View File

@@ -0,0 +1,427 @@
import type { FAQItem } from "@/lib/types";
import type { Metadata } from "next";
import Link from "next/link";
import {
ArrowRight,
CheckCircle2,
Compass,
Link2,
Radar,
Sparkles,
} from "lucide-react";
import Breadcrumbs, { BreadcrumbItem } from "@/components/Breadcrumbs";
import SeoJsonLd from "@/components/SeoJsonLd";
import { FAQSection } from "@/components/aeo/FAQSection";
import {
MarketingPageTracker,
TrackedCtaLink,
} from "@/components/marketing/MarketingAnalytics";
import { AnswerFirstBlock } from "@/components/marketing/AnswerFirstBlock";
import { Button } from "@/components/ui/Button";
import { Card } from "@/components/ui/Card";
import { breadcrumbSchema, faqPageSchema } from "@/lib/schema";
type LinkCard = {
href: string;
title: string;
description: string;
};
type UseCasePageTemplateProps = {
title: string;
description: string;
eyebrow: string;
intro: string;
pageType: "commercial" | "use_case";
cluster: string;
useCase?: string;
breadcrumbs: BreadcrumbItem[];
answer: string;
whenToUse: string[];
comparisonItems: {
label: string;
value: boolean;
text?: string;
}[];
howToSteps: string[];
primaryCta: {
href: string;
label: string;
};
secondaryCta: {
href: string;
label: string;
};
workflowTitle: string;
workflowIntro: string;
workflowCards: {
title: string;
description: string;
}[];
checklistTitle: string;
checklist: string[];
supportLinks: LinkCard[];
faq: FAQItem[];
schemaData?: Record<string, unknown>[];
};
export function buildUseCaseMetadata({
title,
description,
canonicalPath,
}: {
title: string;
description: string;
canonicalPath: string;
}): Metadata {
const canonical = `https://www.qrmaster.net${canonicalPath}`;
return {
title: {
absolute: `${title} | QR Master`,
},
description,
alternates: {
canonical,
languages: {
"x-default": canonical,
en: canonical,
},
},
openGraph: {
title: `${title} | QR Master`,
description,
url: canonical,
type: "website",
images: ["/og-image.png"],
},
twitter: {
title: `${title} | QR Master`,
description,
},
};
}
export function UseCasePageTemplate({
title,
description,
eyebrow,
intro,
pageType,
cluster,
useCase,
breadcrumbs,
answer,
whenToUse,
comparisonItems,
howToSteps,
primaryCta,
secondaryCta,
workflowTitle,
workflowIntro,
workflowCards,
checklistTitle,
checklist,
supportLinks,
faq,
schemaData = [],
}: UseCasePageTemplateProps) {
return (
<>
<SeoJsonLd
data={[...schemaData, breadcrumbSchema(breadcrumbs), faqPageSchema(faq)]}
/>
<MarketingPageTracker
pageType={pageType}
cluster={cluster}
useCase={useCase}
/>
<div className="min-h-screen bg-white">
<section className="relative overflow-hidden bg-gradient-to-br from-slate-950 via-blue-950 to-cyan-900 text-white">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(125,211,252,0.22),transparent_38%),radial-gradient(circle_at_bottom_right,rgba(255,255,255,0.08),transparent_30%)]" />
<div className="relative container mx-auto max-w-7xl px-4 py-20 sm:px-6 lg:px-8">
<Breadcrumbs
items={breadcrumbs}
className="[&_a]:text-blue-100/80 [&_a:hover]:text-white [&_span]:text-blue-100/80 [&_[aria-current=page]]:text-white"
/>
<div className="grid gap-12 lg:grid-cols-[minmax(0,1.2fr)_minmax(320px,0.8fr)] lg:items-center">
<div className="space-y-8">
<div className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/10 px-4 py-2 text-sm font-semibold text-cyan-100 shadow-lg shadow-cyan-950/30 backdrop-blur">
<Sparkles className="h-4 w-4" />
<span>{eyebrow}</span>
</div>
<div className="space-y-5">
<h1 className="max-w-4xl text-4xl font-bold tracking-tight text-white md:text-5xl lg:text-6xl">
{title}
</h1>
<p className="max-w-3xl text-lg leading-8 text-blue-50/88 md:text-xl">
{intro}
</p>
</div>
<div className="grid gap-3 text-sm text-blue-50/80 sm:grid-cols-2">
{[
"Built for QR workflows where the printed surface should stay stable.",
"Focused on operational clarity, not inflated ROI claims.",
"Connected to a commercial parent and sibling workflows.",
"Designed to fit QR Master's existing marketing theme.",
].map((line) => (
<div
key={line}
className="flex items-start gap-3 rounded-2xl border border-white/10 bg-white/5 px-4 py-3 backdrop-blur-sm"
>
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-cyan-300" />
<span>{line}</span>
</div>
))}
</div>
<div className="flex flex-col gap-4 sm:flex-row">
<TrackedCtaLink
href={primaryCta.href}
ctaLabel={primaryCta.label}
ctaLocation="hero_primary"
pageType={pageType}
cluster={cluster}
useCase={useCase}
>
<Button
size="lg"
className="w-full bg-white px-8 py-4 text-base font-semibold text-slate-950 hover:bg-slate-100 sm:w-auto"
>
{primaryCta.label}
</Button>
</TrackedCtaLink>
<TrackedCtaLink
href={secondaryCta.href}
ctaLabel={secondaryCta.label}
ctaLocation="hero_secondary"
pageType={pageType}
cluster={cluster}
useCase={useCase}
>
<Button
variant="outline"
size="lg"
className="w-full border-white/30 bg-white/5 px-8 py-4 text-base text-white hover:bg-white/10 sm:w-auto"
>
{secondaryCta.label}
</Button>
</TrackedCtaLink>
</div>
</div>
<Card className="border-white/10 bg-white/10 p-8 text-white shadow-2xl shadow-slate-950/30 backdrop-blur">
<div className="space-y-6">
<div className="flex items-center justify-between border-b border-white/10 pb-4">
<div>
<div className="text-xs uppercase tracking-[0.24em] text-cyan-200/70">
Workflow snapshot
</div>
<div className="mt-2 text-2xl font-semibold text-white">
What matters here
</div>
</div>
<Compass className="h-9 w-9 text-cyan-300" />
</div>
<div className="space-y-4">
{workflowCards.map((card, index) => (
<div
key={card.title}
className="rounded-2xl border border-white/10 bg-slate-950/30 p-4"
>
<div className="mb-2 flex items-center gap-3">
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-cyan-400/15 text-sm font-semibold text-cyan-200">
{index + 1}
</div>
<div className="text-lg font-semibold text-white">
{card.title}
</div>
</div>
<p className="text-sm leading-6 text-blue-50/80">
{card.description}
</p>
</div>
))}
</div>
</div>
</Card>
</div>
</div>
</section>
<div className="container mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
<AnswerFirstBlock
whatIsIt={answer}
whenToUse={whenToUse}
comparison={{
leftTitle: "Static",
rightTitle: "Better fit here",
items: comparisonItems,
}}
howTo={{
steps: howToSteps,
}}
className="mt-0"
/>
</div>
<section className="bg-slate-50 py-16">
<div className="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mb-10 max-w-3xl">
<h2 className="text-3xl font-bold tracking-tight text-slate-900">
{workflowTitle}
</h2>
<p className="mt-4 text-lg leading-8 text-slate-600">
{workflowIntro}
</p>
</div>
<div className="grid gap-6 lg:grid-cols-3">
{workflowCards.map((card) => (
<Card
key={card.title}
className="rounded-3xl border-slate-200/80 bg-white p-7 shadow-sm"
>
<div className="mb-5 flex h-12 w-12 items-center justify-center rounded-2xl bg-blue-50 text-blue-700">
<Radar className="h-6 w-6" />
</div>
<h3 className="text-xl font-semibold text-slate-900">
{card.title}
</h3>
<p className="mt-3 text-base leading-7 text-slate-600">
{card.description}
</p>
</Card>
))}
</div>
</div>
</section>
<section className="py-16">
<div className="container mx-auto grid max-w-7xl gap-8 px-4 sm:px-6 lg:grid-cols-[minmax(0,0.95fr)_minmax(280px,0.8fr)] lg:px-8">
<Card className="rounded-3xl border-slate-200 bg-white p-8 shadow-sm">
<div className="flex items-start justify-between gap-4">
<div>
<div className="text-sm font-semibold uppercase tracking-[0.22em] text-blue-700">
Checklist
</div>
<h2 className="mt-3 text-3xl font-bold text-slate-900">
{checklistTitle}
</h2>
</div>
<CheckCircle2 className="h-8 w-8 text-blue-700" />
</div>
<ul className="mt-8 space-y-4">
{checklist.map((item) => (
<li
key={item}
className="flex items-start gap-3 text-slate-700"
>
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-green-600" />
<span className="leading-7">{item}</span>
</li>
))}
</ul>
</Card>
<Card className="rounded-3xl border-slate-200 bg-slate-950 p-8 text-white shadow-xl shadow-slate-200">
<div className="flex items-center gap-3">
<Link2 className="h-5 w-5 text-cyan-300" />
<h2 className="text-2xl font-bold">Related links</h2>
</div>
<div className="mt-6 space-y-4">
{supportLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="group block rounded-2xl border border-white/10 bg-white/5 p-4 transition-colors hover:bg-white/10"
>
<div className="flex items-start justify-between gap-4">
<div>
<div className="text-lg font-semibold text-white">
{link.title}
</div>
<div className="mt-2 text-sm leading-6 text-blue-50/78">
{link.description}
</div>
</div>
<ArrowRight className="mt-1 h-4 w-4 shrink-0 text-cyan-300 transition-transform group-hover:translate-x-1" />
</div>
</Link>
))}
</div>
</Card>
</div>
</section>
<div className="container mx-auto max-w-5xl px-4 pb-6 sm:px-6 lg:px-8">
<FAQSection items={faq} title={`${title} FAQ`} />
</div>
<section className="pb-20 pt-6">
<div className="container mx-auto max-w-5xl px-4 sm:px-6 lg:px-8">
<div className="rounded-[2rem] bg-gradient-to-r from-blue-700 via-indigo-700 to-slate-900 px-8 py-10 text-white shadow-2xl shadow-blue-100">
<div className="flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between">
<div className="max-w-2xl">
<div className="text-sm font-semibold uppercase tracking-[0.22em] text-blue-100/80">
Next step
</div>
<h2 className="mt-3 text-3xl font-bold tracking-tight">
Use a QR workflow that stays useful after the print run starts.
</h2>
<p className="mt-4 text-lg leading-8 text-blue-50/84">
{description}
</p>
</div>
<div className="flex flex-col gap-4 sm:flex-row">
<TrackedCtaLink
href={primaryCta.href}
ctaLabel={primaryCta.label}
ctaLocation="footer_primary"
pageType={pageType}
cluster={cluster}
useCase={useCase}
>
<Button
size="lg"
className="w-full bg-white px-7 text-slate-950 hover:bg-slate-100 sm:w-auto"
>
{primaryCta.label}
</Button>
</TrackedCtaLink>
<TrackedCtaLink
href={secondaryCta.href}
ctaLabel={secondaryCta.label}
ctaLocation="footer_secondary"
pageType={pageType}
cluster={cluster}
useCase={useCase}
>
<Button
variant="outline"
size="lg"
className="w-full border-white/30 bg-white/5 text-white hover:bg-white/10 sm:w-auto"
>
{secondaryCta.label}
</Button>
</TrackedCtaLink>
</div>
</div>
</div>
</div>
</section>
</div>
</>
);
}

View File

@@ -47,10 +47,11 @@ export function Footer({ variant = 'marketing', t }: FooterProps) {
<li><Link href="/press" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Press</Link></li>
<li><Link href="/testimonials" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Testimonials</Link></li>
<li><Link href="/authors/timo" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Timo Knuth (Author)</Link></li>
<li><Link href="/#pricing" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.pricing}</Link></li>
<li><Link href="/qr-code-tracking" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>QR Analytics</Link></li>
<li><Link href="/faq" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.faq}</Link></li>
<li><Link href="/blog" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.blog}</Link></li>
<li><Link href="/#pricing" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.pricing}</Link></li>
<li><Link href="/qr-code-tracking" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>QR Analytics</Link></li>
<li><Link href="/use-cases" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Use Cases</Link></li>
<li><Link href="/faq" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.faq}</Link></li>
<li><Link href="/blog" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.blog}</Link></li>
</ul>
</div>
@@ -66,9 +67,10 @@ export function Footer({ variant = 'marketing', t }: FooterProps) {
<li><Link href="/reprint-calculator" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Reprint Cost Calculator</Link></li>
<li><Link href="/qr-code-tracking" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Our Analytics</Link></li>
<li><Link href="/manage-qr-codes" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Manage QR Codes</Link></li>
<li><Link href="/custom-qr-code-generator" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Custom QR</Link></li>
<li><Link href="/tools/barcode-generator" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Barcode Generator</Link></li>
<li><Link href="/manage-qr-codes" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Manage QR Codes</Link></li>
<li><Link href="/custom-qr-code-generator" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Custom QR</Link></li>
<li><Link href="/qr-code-for-marketing-campaigns" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Campaign QR Codes</Link></li>
<li><Link href="/tools/barcode-generator" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Barcode Generator</Link></li>
<li><Link href="/guide/tracking-analytics" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Tracking Guide</Link></li>
<li><Link href="/guide/qr-code-best-practices" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Best Practices</Link></li>
<li><Link href="/guide/bulk-qr-code-generation" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Bulk Generation Guide</Link></li>

View File

@@ -48,7 +48,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> January 5, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> January 5, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -98,7 +98,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> January 5, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> January 5, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -148,7 +148,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> January 5, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> January 5, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -197,7 +197,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> January 5, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> January 5, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -246,7 +246,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> October 18, 2025 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> October 18, 2025 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
<p>Coming soon: How to create trackable QR codes.</p></div>`,
@@ -291,7 +291,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> October 17, 2025 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> October 17, 2025 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -339,7 +339,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> October 16, 2025 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> October 16, 2025 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -392,7 +392,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> October 16, 2025 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> October 16, 2025 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -446,7 +446,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> January 29, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> January 29, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
<h2>Free Barcode Generator</h2><p>Content coming soon.</p></div>`
@@ -499,7 +499,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 1, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 1, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -609,7 +609,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 4, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 4, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -719,7 +719,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 7, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 7, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -839,7 +839,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 10, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 10, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -867,13 +867,6 @@ export const blogPosts: BlogPost[] = [
<li>Best for “permanent” usage like Wi-Fi credentials</li>
</ul>
<h3>Trackable / dynamic QR code</h3>
<ul>
<li>Encodes a short redirect URL</li>
<li>Redirect logs scan events</li>
<li>Destination can be updated</li>
<li>Perfect for campaigns and printed materials</li>
</ul>
<h3>Trackable / dynamic QR code</h3>
<ul>
<li>Encodes a short redirect URL</li>
@@ -984,7 +977,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 13, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 13, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -1168,7 +1161,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 16, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 16, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -1321,7 +1314,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 19, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 19, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -1484,7 +1477,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 22, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 22, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
<h2>Design Tips</h2><p>Content coming soon.</p></div>`
@@ -1533,7 +1526,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 25, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 25, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -1716,7 +1709,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> February 28, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> February 28, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -1859,7 +1852,7 @@ export const blogPosts: BlogPost[] = [
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> March 3, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> March 3, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -2056,7 +2049,7 @@ Authorization: Bearer YOUR_API_KEY</code></pre>
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> March 6, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> March 6, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>
@@ -2219,7 +2212,7 @@ Authorization: Bearer YOUR_API_KEY</code></pre>
<div class="post-metadata bg-blue-50 p-4 rounded-lg mb-8 border-l-4 border-blue-500">
<p class="text-sm text-gray-700">
<strong>Author:</strong> Timo Knuth, QR Code & Marketing Expert<br/>
📅 <strong>Published:</strong> March 9, 2026 | <strong>Last updated:</strong> undefined NaN, NaN
📅 <strong>Published:</strong> March 9, 2026 | <strong>Last updated:</strong> January 26, 2026
</p>
</div>

View File

@@ -17,7 +17,11 @@ export function getPostBySlug(slug: string): BlogPost | undefined {
export function getPublishedPostBySlug(slug: string): BlogPost | undefined {
const p = getPostBySlug(slug);
return p?.published ? p : undefined;
if (!p?.published) return undefined;
const currentDate = new Date();
const publishDate = p.datePublished ? new Date(p.datePublished) : new Date(p.date);
return publishDate <= currentDate ? p : undefined;
}
export function getPostsByPillar(pillar: PillarKey): BlogPost[] {

402
src/lib/growth-pages.ts Normal file
View File

@@ -0,0 +1,402 @@
import type { FAQItem } from "@/lib/types";
export type CommercialPageLink = {
href: string;
title: string;
description: string;
accent: string;
};
export type UseCaseLink = {
slug: string;
href: string;
title: string;
cluster: string;
summary: string;
parentHref: string;
parentTitle: string;
ctaLabel: string;
};
export type SupportResourceLink = {
href: string;
title: string;
description: string;
};
export type UseCasePageContent = UseCaseLink & {
eyebrow: string;
titleSuffix: string;
metaDescription: string;
intro: string;
answer: string;
whenToUse: string[];
comparisonItems: {
label: string;
value: boolean;
text?: string;
}[];
howToSteps: string[];
workflowTitle: string;
workflowIntro: string;
workflowCards: {
title: string;
description: string;
}[];
checklistTitle: string;
checklist: string[];
supportLinks: SupportResourceLink[];
faq: FAQItem[];
};
export const commercialPages: CommercialPageLink[] = [
{
href: "/dynamic-qr-code-generator",
title: "Dynamic QR Code Generator",
description: "Edit the destination after print and keep one QR live across changing campaigns.",
accent: "from-blue-600 to-cyan-500",
},
{
href: "/qr-code-tracking",
title: "QR Code Tracking",
description: "Measure scans by placement, device, and timing when you need proof instead of guesswork.",
accent: "from-indigo-600 to-blue-500",
},
{
href: "/custom-qr-code-generator",
title: "Custom QR Code Generator",
description: "Match printed QR codes to your brand system without losing scannability.",
accent: "from-sky-600 to-indigo-500",
},
{
href: "/bulk-qr-code-generator",
title: "Bulk QR Code Generator",
description: "Create large sets for labels, event materials, and repeatable offline workflows.",
accent: "from-cyan-600 to-sky-500",
},
{
href: "/qr-code-for-marketing-campaigns",
title: "QR Codes for Marketing Campaigns",
description: "Plan campaign QR workflows around attribution, creative testing, and print distribution.",
accent: "from-slate-800 to-blue-600",
},
];
export const featuredUseCases: UseCaseLink[] = [
{
slug: "restaurant-menu-qr-codes",
href: "/use-cases/restaurant-menu-qr-codes",
title: "Restaurant Menu QR Codes",
cluster: "restaurants",
summary: "Keep printed table cards useful when menu links, prices, specials, or hours change.",
parentHref: "/dynamic-qr-code-generator",
parentTitle: "Dynamic QR Code Generator",
ctaLabel: "Create your restaurant menu QR",
},
{
slug: "business-card-qr-codes",
href: "/use-cases/business-card-qr-codes",
title: "Business Card QR Codes",
cluster: "business-cards",
summary: "Send contacts to a current profile, booking page, or vCard without reprinting cards.",
parentHref: "/dynamic-qr-code-generator",
parentTitle: "Dynamic QR Code Generator",
ctaLabel: "Create your business card QR",
},
{
slug: "event-qr-codes",
href: "/use-cases/event-qr-codes",
title: "Event QR Codes",
cluster: "events",
summary: "Split operational and campaign QR flows so schedules and tracking stay manageable on event day.",
parentHref: "/qr-code-tracking",
parentTitle: "QR Code Tracking",
ctaLabel: "Create your event QR code",
},
];
export const upcomingUseCaseIdeas: SupportResourceLink[] = [
{
href: "/bulk-qr-code-generator",
title: "Packaging and label workflows",
description: "Best next cluster for bulk creation, manuals, inserts, and recurring product updates.",
},
{
href: "/qr-code-for-marketing-campaigns",
title: "Flyer and brochure campaigns",
description: "Strong print-intent cluster once the marketing-campaign commercial parent is live.",
},
{
href: "/qr-code-tracking",
title: "Feedback and coupon journeys",
description: "Good second-wave pages once tracking and CTA attribution are stable.",
},
];
export const supportResources: SupportResourceLink[] = [
{
href: "/learn/use-cases",
title: "Learn: Use Cases",
description: "Editorial pillar page for educational browsing and broader QR workflow discovery.",
},
{
href: "/blog/dynamic-vs-static-qr-codes",
title: "Dynamic vs Static QR Codes",
description: "Explainer for the operational difference between fixed and editable QR destinations.",
},
{
href: "/blog/utm-parameter-qr-codes",
title: "UTM Parameters with QR Codes",
description: "Reference guide for campaign naming, placement attribution, and offline measurement.",
},
];
export const useCasePageContent: Record<string, UseCasePageContent> = {
"restaurant-menu-qr-codes": {
...featuredUseCases[0],
eyebrow: "Restaurants",
titleSuffix: "for Restaurants, Cafes, and Changing Menus",
metaDescription:
"Use restaurant menu QR codes to keep printed table cards useful when menu links, pricing, or specials change.",
intro:
"Restaurant menu QR codes work best when the printed code stays the same but the menu destination can change as your service changes.",
answer:
"A restaurant menu QR code should point to a mobile-friendly menu that you can update without replacing every printed card, flyer, or table tent.",
whenToUse: [
"Your menu link changes seasonally, weekly, or during service.",
"You want one printed QR on tables, windows, takeaway inserts, or flyers.",
"You need to route customers to the right menu, order flow, or special page without reprinting.",
],
comparisonItems: [
{ label: "Menu destination", text: "Fixed once printed", value: true },
{ label: "Last-minute updates", text: "Reprint required", value: true },
{ label: "Campaign tracking", text: "Limited", value: true },
],
howToSteps: [
"Create one dynamic menu QR and place it on your printed surfaces.",
"Send scanners to your current menu, order page, or daily specials page.",
"Update the destination when the menu or campaign changes instead of replacing the code.",
],
workflowTitle: "What a good restaurant QR setup should handle",
workflowIntro:
"The QR code is not the product. The workflow behind it is. A good restaurant setup keeps print stable while operations stay flexible.",
workflowCards: [
{
title: "Stable table cards",
description: "Keep one printed QR on tables and point it to the current menu, lunch card, or ordering page.",
},
{
title: "Seasonal offers",
description: "Swap specials, tasting menus, or holiday landing pages without touching the printed material.",
},
{
title: "Placement tracking",
description: "Use different menu or campaign destinations by location so you can compare tables, windows, and takeaway inserts.",
},
],
checklistTitle: "Restaurant QR checklist",
checklist: [
"Use a mobile-first landing page instead of a hard-to-read PDF where possible.",
"Keep the same printed QR on every stable surface you do not want to replace often.",
"Use CTA copy like 'Scan for menu' or 'Scan for today's specials' so the scan intent is obvious.",
"Pair campaign placements with trackable destinations when you test takeaway or window traffic.",
],
supportLinks: [
{
href: "/dynamic-qr-code-generator",
title: "Commercial parent: Dynamic QR Code Generator",
description: "Best fit when the real need is editing the destination after print.",
},
{
href: "/blog/qr-code-restaurant-menu",
title: "Restaurant menu guide",
description: "Existing editorial asset with menu placement and implementation basics.",
},
{
href: "/use-cases/business-card-qr-codes",
title: "Sibling page: Business Card QR Codes",
description: "Useful example of another print-first workflow where the destination changes over time.",
},
],
faq: [
{
question: "Should a restaurant menu QR code be static or dynamic?",
answer: "Use a dynamic QR code when the menu destination may change. That lets you update the landing page without replacing your printed materials.",
},
{
question: "What should a restaurant menu QR code link to?",
answer: "Link to a mobile-friendly menu page, ordering page, or a short service hub that helps customers reach the right menu fast.",
},
{
question: "Can I use one restaurant QR code in multiple places?",
answer: "Yes. One stable code can be reused across table cards, flyers, and takeaway materials, especially when the destination is managed dynamically.",
},
],
},
"business-card-qr-codes": {
...featuredUseCases[1],
eyebrow: "Business Cards",
titleSuffix: "for Contact Sharing, Bookings, and Portfolio Links",
metaDescription:
"Use business card QR codes to share a current contact page, vCard, booking link, or portfolio without reprinting cards.",
intro:
"Business card QR codes are most useful when your contact destination changes faster than your print inventory.",
answer:
"A business card QR code should send people to the best next action today, whether that is saving your contact, opening a booking link, or visiting a current profile page.",
whenToUse: [
"Your role, booking link, or portfolio changes more often than your printed cards.",
"You want one card to work for networking, sales follow-up, and contact saving.",
"You need a cleaner handoff than asking people to type a URL from a small card.",
],
comparisonItems: [
{ label: "Destination flexibility", text: "Fixed once printed", value: true },
{ label: "Contact updates", text: "New cards needed", value: true },
{ label: "Action routing", text: "Single fixed page", value: true },
],
howToSteps: [
"Choose the real action you want after the scan: save contact, book time, or view work.",
"Generate a QR code that sends people to that destination or to a vCard-capable landing page.",
"Keep the print the same and update the linked destination when your details evolve.",
],
workflowTitle: "Where business card QR codes pay off",
workflowIntro:
"Printed cards still work. The problem is that the destination behind them often gets stale first.",
workflowCards: [
{
title: "Current contact flow",
description: "Send scanners to a vCard or current contact page so the next step is saving your details, not typing them.",
},
{
title: "Role or profile updates",
description: "Update the destination if you change company, title, offer, or booking link while old cards are still in circulation.",
},
{
title: "Context-aware follow-up",
description: "Point event-specific cards or team variants to the most relevant landing page instead of one generic homepage.",
},
],
checklistTitle: "Business card QR checklist",
checklist: [
"Pick one primary post-scan action instead of trying to send every scanner to everything at once.",
"Make the landing page useful on mobile because most business-card scans happen on phones.",
"Use CTA text such as 'Scan to save my contact' or 'Scan to book a call'.",
"Test the print size and contrast before ordering a large run.",
],
supportLinks: [
{
href: "/dynamic-qr-code-generator",
title: "Commercial parent: Dynamic QR Code Generator",
description: "Use when the printed card stays constant but the best destination changes.",
},
{
href: "/tools/vcard-qr-code",
title: "vCard QR Code tool",
description: "Free tool for contact-saving workflows when a vCard is the best immediate action.",
},
{
href: "/use-cases/event-qr-codes",
title: "Sibling page: Event QR Codes",
description: "Another workflow where temporary destinations and scan context matter.",
},
],
faq: [
{
question: "What should a business card QR code link to?",
answer: "The best destination is the one next step you want most: a vCard, booking page, contact hub, or current portfolio page.",
},
{
question: "Do business card QR codes need to be dynamic?",
answer: "They should be dynamic if your destination may change over the life of the printed card. That keeps old cards useful longer.",
},
{
question: "Can a business card QR code send people straight to contact saving?",
answer: "Yes. A vCard QR or a landing page with clear save-contact options is often the simplest and most practical post-scan action.",
},
],
},
"event-qr-codes": {
...featuredUseCases[2],
eyebrow: "Events",
titleSuffix: "for Check-In, Schedules, Booths, and Campaign Tracking",
metaDescription:
"Use event QR codes for schedules, check-in flows, and trackable campaign placements across signs, flyers, and booths.",
intro:
"Event QR codes work best when you separate operational QR flows from promotional ones and keep your printed placements easy to manage.",
answer:
"A good event QR setup uses different QR destinations for different jobs: operations, attendee utility, and campaign measurement should not all depend on one code.",
whenToUse: [
"Your event schedule, map, or resources may change close to event day.",
"You want to compare booth, banner, flyer, or badge placements instead of treating every scan as one bucket.",
"You need one event QR system that supports both attendee utility and marketing follow-up.",
],
comparisonItems: [
{ label: "Schedule changes", text: "New print may be needed", value: true },
{ label: "Placement reporting", text: "Weak by default", value: true },
{ label: "Operational vs campaign flows", text: "Often mixed", value: true },
],
howToSteps: [
"Split event QR codes by purpose: check-in, attendee info, and campaign placements.",
"Use trackable destinations for banners, booth assets, and flyers where placement performance matters.",
"Keep fast-changing resources on destinations you can update without replacing the printed code.",
],
workflowTitle: "Event workflows worth designing on purpose",
workflowIntro:
"Events generate scans in very different contexts. Treating them as one generic QR use case leaves both operations and measurement weaker.",
workflowCards: [
{
title: "Operational QR flows",
description: "Use dedicated QR paths for check-in, schedules, maps, and attendee resources that may shift before or during the event.",
},
{
title: "Booth and banner tracking",
description: "Track scans from distinct placements so you can compare booth creatives, sponsor zones, or call-to-action angles.",
},
{
title: "Post-event follow-up",
description: "Route scanners to the most relevant recap, booking, or lead capture page after the event without changing the printed assets.",
},
],
checklistTitle: "Event QR checklist",
checklist: [
"Do not force one QR code to handle operations, schedule updates, and lead-gen at the same time.",
"Use descriptive CTA copy like 'Scan for agenda' or 'Scan for booth resources'.",
"Track campaign placements separately so booth banners and flyers are comparable.",
"Test glare, print size, and placement distance on the real event materials before the event starts.",
],
supportLinks: [
{
href: "/qr-code-tracking",
title: "Commercial parent: QR Code Tracking",
description: "Best fit when the priority is measuring placement and scan context across the event.",
},
{
href: "/tools/event-qr-code",
title: "Event QR Code tool",
description: "Useful for save-the-date and calendar workflows that sit alongside broader event QR strategy.",
},
{
href: "/use-cases/restaurant-menu-qr-codes",
title: "Sibling page: Restaurant Menu QR Codes",
description: "Another example of a printed workflow where the content behind the QR changes often.",
},
],
faq: [
{
question: "Should an event use one QR code or several?",
answer: "Several is usually better. Separate operational QR codes from campaign QR codes so schedules, check-in, and attribution do not compete for one destination.",
},
{
question: "Can event QR codes be updated after print?",
answer: "Yes, if the destination is managed dynamically. That is useful for schedules, resource hubs, and post-event follow-up pages.",
},
{
question: "How do I measure which event placement performs best?",
answer: "Use distinct destinations or tagged URLs for each placement so banner, booth, badge, and flyer traffic can be compared cleanly.",
},
],
},
};
export function getUseCasePage(slug: string): UseCasePageContent | undefined {
return useCasePageContent[slug];
}

View File

@@ -62,27 +62,27 @@ export function getAllIndexableUrls(): string[] {
const blogPages = blogPosts.map(post => `${baseUrl}/blog/${post.slug}`);
// Main pages (synced with sitemap.ts)
const mainPages = [
baseUrl,
`${baseUrl}/qr-code-erstellen`,
`${baseUrl}/qr-code-tracking`,
`${baseUrl}/reprint-calculator`,
`${baseUrl}/dynamic-qr-code-generator`,
`${baseUrl}/bulk-qr-code-generator`,
const mainPages = [
baseUrl,
`${baseUrl}/about`,
`${baseUrl}/contact`,
`${baseUrl}/press`,
`${baseUrl}/testimonials`,
`${baseUrl}/qr-code-erstellen`,
`${baseUrl}/qr-code-tracking`,
`${baseUrl}/reprint-calculator`,
`${baseUrl}/dynamic-qr-code-generator`,
`${baseUrl}/bulk-qr-code-generator`,
`${baseUrl}/custom-qr-code-generator`,
`${baseUrl}/manage-qr-codes`,
`${baseUrl}/pricing`,
`${baseUrl}/tools`,
`${baseUrl}/features`,
`${baseUrl}/faq`,
`${baseUrl}/blog`,
`${baseUrl}/signup`,
`${baseUrl}/login`,
`${baseUrl}/privacy`,
`${baseUrl}/guide/tracking-analytics`,
`${baseUrl}/guide/bulk-qr-code-generation`,
`${baseUrl}/guide/qr-code-best-practices`,
];
`${baseUrl}/manage-qr-codes`,
`${baseUrl}/pricing`,
`${baseUrl}/tools`,
`${baseUrl}/features`,
`${baseUrl}/faq`,
`${baseUrl}/blog`,
`${baseUrl}/privacy`,
`${baseUrl}/newsletter`,
];
// Learn hub and pillar pages
const learnPages = [

View File

@@ -11,12 +11,15 @@ export function middleware(req: NextRequest) {
if (path === '/guide/bulk-qr-code-generation') {
return NextResponse.redirect(new URL('/learn/developer', req.url), 301);
}
if (path === '/guide/qr-code-best-practices') {
return NextResponse.redirect(new URL('/learn/basics', req.url), 301);
}
// Public routes that don't require authentication
const publicPaths = [
if (path === '/guide/qr-code-best-practices') {
return NextResponse.redirect(new URL('/learn/basics', req.url), 301);
}
if (path === '/create-qr') {
return NextResponse.redirect(new URL('/dynamic-qr-code-generator', req.url), 301);
}
// Public routes that don't require authentication
const publicPaths = [
'/',
'/pricing',
'/faq',
@@ -42,11 +45,13 @@ export function middleware(req: NextRequest) {
'/display',
'/contact',
'/about',
'/learn',
'/authors',
'/press',
'/testimonials',
];
'/learn',
'/use-cases',
'/authors',
'/press',
'/testimonials',
'/qr-code-for-marketing-campaigns',
];
// Check if path is public
const isPublicPath = publicPaths.some(p => path === p || path.startsWith(p + '/'));
@@ -95,4 +100,4 @@ export const config = {
*/
'/((?!_next/static|_next/image|favicon.ico|logo.svg|og-image.png).*)',
],
};
};