Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";
import "./.next/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
59 changes: 59 additions & 0 deletions public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# robots.txt for DevPockit - Developer Tools Web App
# https://devpockit.hypkey.com

# ===========================================
# Default rules for all crawlers
# ===========================================
User-agent: *

# Allow CSS/JS for proper page rendering (important for SEO)
Allow: /_next/static/

# Block Next.js build internals (not useful for search)
Disallow: /_next/data/
Disallow: /_next/image

# Block API routes
Disallow: /api/

# Block user-specific tool instances (session URLs)
Disallow: /tools/*/*/*/

# ===========================================
# Google-specific rules (faster crawling)
# ===========================================
User-agent: Googlebot
Allow: /

# ===========================================
# Bing-specific rules
# ===========================================
User-agent: Bingbot
Allow: /
Crawl-delay: 1

# ===========================================
# AI Crawlers (uncomment to block if desired)
# ===========================================
# User-agent: GPTBot
# Disallow: /

# User-agent: ChatGPT-User
# Disallow: /

# User-agent: Claude-Web
# Disallow: /

# User-agent: Anthropic-AI
# Disallow: /

# User-agent: Google-Extended
# Disallow: /

# ===========================================
# Sitemap
# ===========================================
Sitemap: https://devpockit.hypkey.com/sitemap.xml

# Canonical host
Host: https://devpockit.hypkey.com
81 changes: 78 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,85 @@ const dmSerifText = DM_Serif_Text({
})

export const metadata: Metadata = {
title: 'DevPockit - Essential Developer Tools',
description: 'Essential dev tools at your fingertips. Work faster with tools that respect your privacy.',
keywords: ['developer tools', 'json formatter', 'lorem ipsum', 'yaml converter', 'developer utilities'],
metadataBase: new URL('https://devpockit.hypkey.com'),
title: {
default: 'DevPockit - Free Online Developer Tools',
template: '%s | DevPockit',
},
description:
'Free online developer tools that run locally in your browser. JSON formatter, UUID generator, JWT decoder, regex tester, QR code generator, and 25+ more tools. Fast, private, no sign-up required.',
keywords: [
// Primary keywords
'developer tools',
'online dev tools',
'free developer tools',
'web developer tools',
// Tool-specific keywords
'json formatter',
'json beautifier',
'uuid generator',
'jwt decoder',
'jwt encoder',
'regex tester',
'qr code generator',
'base64 encoder',
'url encoder decoder',
'cron expression parser',
'timestamp converter',
'xml formatter',
'yaml converter',
'hash generator',
'cidr calculator',
'diff checker',
'lorem ipsum generator',
// Feature keywords
'browser-based tools',
'privacy-focused',
'no sign-up',
'offline capable',
],
authors: [{ name: 'DevPockit Team' }],
creator: 'DevPockit',
publisher: 'DevPockit',
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
openGraph: {
type: 'website',
locale: 'en_US',
url: 'https://devpockit.hypkey.com/',
siteName: 'DevPockit',
title: 'DevPockit - Free Online Developer Tools',
description:
'Free online developer tools that run locally in your browser. JSON formatter, UUID generator, JWT decoder, and 25+ more tools. Fast, private, no sign-up.',
images: [
{
url: '/og-image.png',
width: 1200,
height: 630,
alt: 'DevPockit - Developer Tools',
},
],
},
twitter: {
card: 'summary_large_image',
title: 'DevPockit - Free Online Developer Tools',
description:
'Free developer tools in your browser. JSON formatter, UUID generator, JWT decoder & more. Private, fast, no sign-up.',
images: ['/og-image.png'],
},
alternates: {
canonical: 'https://devpockit.hypkey.com/',
},
category: 'technology',
}

export const viewport: Viewport = {
Expand Down
55 changes: 55 additions & 0 deletions src/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { MetadataRoute } from 'next';
import { toolCategories } from '@/libs/tools-data';

// Required for static export (GitHub Pages)
export const dynamic = 'force-static';

const BASE_URL = 'https://devpockit.hypkey.com';

// Helper to ensure trailing slash (required for GitHub Pages)
const withTrailingSlash = (url: string) => (url.endsWith('/') ? url : `${url}/`);

export default function sitemap(): MetadataRoute.Sitemap {
// Static pages
const staticPages: MetadataRoute.Sitemap = [
{
url: `${BASE_URL}/`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 1.0,
},
{
url: `${BASE_URL}/about/`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${BASE_URL}/tools/`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.9,
},
];

// Category pages
const categoryPages: MetadataRoute.Sitemap = toolCategories.map((category) => ({
url: `${BASE_URL}/tools/${category.id}/`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: 0.8,
}));

// Individual tool pages
const toolPages: MetadataRoute.Sitemap = toolCategories.flatMap((category) =>
category.tools.map((tool) => ({
url: withTrailingSlash(`${BASE_URL}${tool.path}`),
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.9,
}))
);

return [...staticPages, ...categoryPages, ...toolPages];
}

62 changes: 61 additions & 1 deletion src/app/tools/[category]/[toolId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getToolById, getTools } from '@/libs/tools-data';
import { getCategoryById, getToolById, getTools } from '@/libs/tools-data';
import { Metadata } from 'next';
import { notFound } from 'next/navigation';

interface ToolPageProps {
Expand All @@ -23,6 +24,65 @@ export async function generateStaticParams() {
}));
}

// Generate SEO-optimized metadata for each tool
export async function generateMetadata({ params }: ToolPageProps): Promise<Metadata> {
const { toolId, category } = await params;
const tool = getToolById(toolId);
const categoryData = getCategoryById(category);

if (!tool) {
return {
title: 'Tool Not Found',
};
}

// Generate tool-specific keywords
const toolKeywords = [
tool.name.toLowerCase(),
`online ${tool.name.toLowerCase()}`,
`free ${tool.name.toLowerCase()}`,
`${tool.name.toLowerCase()} online`,
`${tool.name.toLowerCase()} tool`,
categoryData?.name.toLowerCase() || category,
'developer tools',
'devpockit',
];

const title = `${tool.name} - Free Online Tool`;
const description = `${tool.description} Free, fast, and runs locally in your browser. No sign-up required.`;

// Ensure trailing slash for GitHub Pages compatibility
const toolUrl = tool.path.endsWith('/') ? tool.path : `${tool.path}/`;

return {
title,
description,
keywords: toolKeywords,
openGraph: {
title: `${tool.name} | DevPockit`,
description,
url: `https://devpockit.hypkey.com${toolUrl}`,
type: 'website',
images: [
{
url: '/og-image.png',
width: 1200,
height: 630,
alt: tool.name,
},
],
},
twitter: {
card: 'summary_large_image',
title: `${tool.name} | DevPockit`,
description,
},
alternates: {
canonical: `https://devpockit.hypkey.com${toolUrl}`,
},
};
}

export default async function ToolPage({ params }: ToolPageProps) {
try {
const { category, toolId } = await params;
Expand Down
87 changes: 87 additions & 0 deletions src/components/seo/JsonLd.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use client';

import { Tool } from '@/types/tools';

interface WebsiteJsonLdProps {
type: 'website';
}

interface ToolJsonLdProps {
type: 'tool';
tool: Tool;
}

interface BreadcrumbJsonLdProps {
type: 'breadcrumb';
items: { name: string; url: string }[];
}

type JsonLdProps = WebsiteJsonLdProps | ToolJsonLdProps | BreadcrumbJsonLdProps;

export function JsonLd(props: JsonLdProps) {
let structuredData: object;

switch (props.type) {
case 'website':
structuredData = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'DevPockit',
description:
'Free online developer tools that run locally in your browser. JSON formatter, UUID generator, JWT decoder, and more.',
url: 'https://devpockit.hypkey.com',
potentialAction: {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: 'https://devpockit.hypkey.com/tools?search={search_term_string}',
},
'query-input': 'required name=search_term_string',
},
};
break;

case 'tool':
structuredData = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: props.tool.name,
description: props.tool.description,
url: `https://devpockit.hypkey.com${props.tool.path}`,
applicationCategory: 'DeveloperApplication',
operatingSystem: 'Web Browser',
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'USD',
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.8',
ratingCount: '100',
},
};
break;

case 'breadcrumb':
structuredData = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: props.items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url,
})),
};
break;
}

return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
);
}

Loading