fix: improve SEO with pre-rendering and dynamic meta tags

Critical SEO improvements to fix Google Search Console indexing:

## Sitemap Updates:
- Added missing Invoice Editor and What's New pages
- Updated all lastmod dates to 2025-10-15
- Increased editor tools priority to 0.9
- Added organizational comments
- Fixed /whats-new route (was /release-notes)

## Pre-rendering Implementation:
- Added react-snap for static HTML generation
- Configured to pre-render all tool pages
- Solves React SPA indexing issue
- Crawlers now see full HTML content

## Dynamic Meta Tags:
- Added react-helmet-async for SEO management
- Created reusable SEO component with:
  - Dynamic titles and descriptions
  - Open Graph tags (Facebook)
  - Twitter Card tags
  - JSON-LD structured data
  - Canonical URLs
- Wrapped App with HelmetProvider
- Added SEO to Home page

## Route Fixes:
- Added /whats-new route (primary)
- Kept /release-notes as fallback
- Consistent routing across app

## Documentation:
- Created comprehensive SEO_FIX_GUIDE.md
- Step-by-step Google Search Console instructions
- Troubleshooting guide
- Timeline expectations
- Testing procedures

These changes will dramatically improve Google indexing and search visibility.
This commit is contained in:
dwindown
2025-10-15 10:01:48 +07:00
parent f60c1d16c8
commit f6c19e855d
7 changed files with 1670 additions and 98 deletions

View File

@@ -1,5 +1,6 @@
import React, { useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { HelmetProvider } from 'react-helmet-async';
import Layout from './components/Layout';
import ErrorBoundary from './components/ErrorBoundary';
import Home from './pages/Home';
@@ -30,31 +31,34 @@ function App() {
}, []);
return (
<ErrorBoundary>
<Router>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/json" element={<JsonTool />} />
<Route path="/serialize" element={<SerializeTool />} />
<Route path="/url" element={<UrlTool />} />
<Route path="/base64" element={<Base64Tool />} />
<Route path="/csv-json" element={<CsvJsonTool />} />
<Route path="/beautifier" element={<BeautifierTool />} />
<Route path="/diff" element={<DiffTool />} />
<Route path="/text-length" element={<TextLengthTool />} />
<Route path="/object-editor" element={<ObjectEditor />} />
<Route path="/table-editor" element={<TableEditor />} />
<Route path="/invoice-editor" element={<InvoiceEditor />} />
<Route path="/invoice-preview" element={<InvoicePreview />} />
<Route path="/invoice-preview-minimal" element={<InvoicePreviewMinimal />} />
<Route path="/release-notes" element={<ReleaseNotes />} />
<Route path="/privacy" element={<PrivacyPolicy />} />
<Route path="/terms" element={<TermsOfService />} />
</Routes>
</Layout>
</Router>
</ErrorBoundary>
<HelmetProvider>
<ErrorBoundary>
<Router>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/json" element={<JsonTool />} />
<Route path="/serialize" element={<SerializeTool />} />
<Route path="/url" element={<UrlTool />} />
<Route path="/base64" element={<Base64Tool />} />
<Route path="/csv-json" element={<CsvJsonTool />} />
<Route path="/beautifier" element={<BeautifierTool />} />
<Route path="/diff" element={<DiffTool />} />
<Route path="/text-length" element={<TextLengthTool />} />
<Route path="/object-editor" element={<ObjectEditor />} />
<Route path="/table-editor" element={<TableEditor />} />
<Route path="/invoice-editor" element={<InvoiceEditor />} />
<Route path="/invoice-preview" element={<InvoicePreview />} />
<Route path="/invoice-preview-minimal" element={<InvoicePreviewMinimal />} />
<Route path="/whats-new" element={<ReleaseNotes />} />
<Route path="/release-notes" element={<ReleaseNotes />} />
<Route path="/privacy" element={<PrivacyPolicy />} />
<Route path="/terms" element={<TermsOfService />} />
</Routes>
</Layout>
</Router>
</ErrorBoundary>
</HelmetProvider>
);
}

75
src/components/SEO.js Normal file
View File

@@ -0,0 +1,75 @@
import React from 'react';
import { Helmet } from 'react-helmet-async';
const SEO = ({
title,
description,
keywords,
path = '/',
type = 'website',
image = 'https://dewe.dev/og-image.png'
}) => {
const siteUrl = 'https://dewe.dev';
const fullUrl = `${siteUrl}${path}`;
const fullTitle = title ? `${title} | Developer Tools` : 'Developer Tools - Essential Web Developer Utilities';
const defaultDescription = 'Free online developer tools for JSON, CSV, Base64, URL encoding, code beautification, and more. Privacy-first, no data storage, all processing in your browser.';
const metaDescription = description || defaultDescription;
const defaultKeywords = 'developer tools, json editor, csv converter, base64 encoder, url encoder, code beautifier, diff tool, web developer utilities, online tools';
const metaKeywords = keywords || defaultKeywords;
return (
<Helmet>
{/* Basic Meta Tags */}
<title>{fullTitle}</title>
<meta name="description" content={metaDescription} />
<meta name="keywords" content={metaKeywords} />
<link rel="canonical" href={fullUrl} />
{/* Open Graph / Facebook */}
<meta property="og:type" content={type} />
<meta property="og:url" content={fullUrl} />
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={metaDescription} />
<meta property="og:image" content={image} />
<meta property="og:site_name" content="Developer Tools" />
{/* Twitter Card */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content={fullUrl} />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={metaDescription} />
<meta name="twitter:image" content={image} />
{/* Additional SEO Tags */}
<meta name="robots" content="index, follow" />
<meta name="googlebot" content="index, follow" />
<meta name="author" content="Developer Tools" />
<meta name="language" content="English" />
{/* JSON-LD Structured Data */}
<script type="application/ld+json">
{JSON.stringify({
"@context": "https://schema.org",
"@type": "WebApplication",
"name": fullTitle,
"description": metaDescription,
"url": fullUrl,
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Any",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"provider": {
"@type": "Organization",
"name": "Developer Tools",
"url": siteUrl
}
})}
</script>
</Helmet>
);
};
export default SEO;

View File

@@ -4,6 +4,7 @@ import { Link } from 'react-router-dom';
import ToolCard from '../components/ToolCard';
import { TOOLS, SITE_CONFIG } from '../config/tools';
import { useAnalytics } from '../hooks/useAnalytics';
import SEO from '../components/SEO';
const Home = () => {
const [searchTerm, setSearchTerm] = useState('');
@@ -32,18 +33,24 @@ const Home = () => {
);
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50 dark:from-slate-900 dark:via-slate-800 dark:to-indigo-900">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Hero Section */}
<div className={`text-center pt-16 pb-20 transition-all duration-1000 ${mounted ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{/* Terminal-style header */}
<div className="inline-flex items-center gap-2 px-4 py-2 bg-slate-800 dark:bg-slate-700 rounded-full text-green-400 font-mono text-sm mb-8 shadow-lg">
<Terminal className="h-4 w-4" />
<span>~/dewe.dev $</span>
<span className="animate-pulse">_</span>
</div>
<h1 className="text-5xl hidden md:text-7xl font-bold bg-gradient-to-r from-blue-600 via-purple-600 to-indigo-600 bg-clip-text text-transparent mb-6">
<>
<SEO
title="Home"
description="Free online developer tools for JSON editing, table manipulation, invoice generation, Base64 encoding, URL encoding, code beautification, and more. Privacy-first, no data storage."
keywords="developer tools, json editor, table editor, invoice generator, base64 encoder, url encoder, code beautifier, diff tool, web developer utilities"
path="/"
/>
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50 dark:from-slate-900 dark:via-slate-800 dark:to-slate-900">
<div className="container mx-auto px-4 py-12">
<div className="text-center mb-16">
{/* Terminal-style header */}
<div className="inline-flex items-center gap-2 px-4 py-2 bg-slate-800 dark:bg-slate-700 rounded-full text-green-400 font-mono text-sm mb-8 shadow-lg">
<Terminal className="h-4 w-4" />
<span>~/dewe.dev $</span>
<span className="animate-pulse">_</span>
</div>
<h1 className="text-5xl hidden md:text-7xl font-bold bg-gradient-to-r from-blue-600 via-purple-600 to-indigo-600 bg-clip-text text-transparent mb-6">
{SITE_CONFIG.title}
</h1>
@@ -215,7 +222,7 @@ const Home = () => {
</div>
</div>
</div>
</div>
</>
);
};