Skip to content

feat: improve SEO/a11y, add Contact route, env API URL, 404 page, rob…#133

Open
MananGilhotra wants to merge 1 commit intoOpen-Source-Kashmir:mainfrom
MananGilhotra:feat/seo-contact-404
Open

feat: improve SEO/a11y, add Contact route, env API URL, 404 page, rob…#133
MananGilhotra wants to merge 1 commit intoOpen-Source-Kashmir:mainfrom
MananGilhotra:feat/seo-contact-404

Conversation

@MananGilhotra
Copy link
Copy Markdown

@MananGilhotra MananGilhotra commented Oct 29, 2025

adds canonical URL and theme-color meta, removes duplicate viewport tag, and includes a “Skip to content” link for better SEO and a11y.

Introduces a friendly 404 page and a catch-all route to guide users when a path doesn’t exist.

Adds robots.txt and sitemap.xml to help search engines crawl and index key pages properly

Defaults PORT to 5002, reads CORS_ORIGIN from env, and adds a /health endpoint for simple uptime checks and safer defaults

Summary by CodeRabbit

  • New Features

    • Added contact form page accessible from the main navigation menu
    • Added 404 Not Found page for invalid routes
  • Documentation

    • Updated setup instructions with required environment configuration step
  • Chores

    • Added SEO support with robots.txt and sitemap.xml
    • Enabled configurable API base URL and CORS origin through environment variables
    • Improved HTML metadata with canonical URL, theme color, and accessibility enhancements

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Oct 29, 2025

Walkthrough

The PR adds environment-driven API configuration, new frontend routes for Contact and 404 pages, backend health check and configurable CORS support, SEO improvements via robots.txt and sitemap, and HTML accessibility enhancements. It establishes prerequisites for development setup including required .env configuration.

Changes

Cohort / File(s) Change Summary
Environment & Setup
README.md
Added step 6 to configure frontend API base URL via .env file with VITE_API_BASE_URL, renumbering subsequent steps
Backend API Enhancements
backend/contact.js
Added PORT fallback (5002), configurable CORS origin (process.env.CORS_ORIGIN), and GET /health endpoint returning status
Frontend Routing
src/App.jsx, src/pages/NotFound.jsx
Added /contact route for Contact component and catch-all "*" route for 404 NotFound page; added main-content id to main element
Frontend Components
src/components/Navbar.jsx, src/components/Contact_Form/Contact.jsx
Added Contact navigation link to desktop and mobile menus; made API base URL configurable via import.meta.env.VITE_API_BASE_URL in Contact form
SEO & HTML Metadata
index.html, public/robots.txt, public/sitemap.xml
Removed duplicate viewport meta tag, added canonical URL and theme-color meta, skip-to-content anchor; introduced robots.txt with allow rules and sitemap reference; added XML sitemap with seven indexed URLs

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FrontendApp as Frontend App
    participant Env as .env / Environment
    participant Backend as Backend (contact.js)

    User->>FrontendApp: Navigates to /contact
    FrontendApp->>Env: Reads VITE_API_BASE_URL
    Env-->>FrontendApp: Returns API base URL (or default http://localhost:5002)
    FrontendApp->>Backend: POST /api/contact (via configured baseUrl)
    note over Backend: CORS check using<br/>process.env.CORS_ORIGIN
    Backend-->>FrontendApp: 200 Success
    FrontendApp-->>User: Form submitted

    User->>Backend: GET /health (health check)
    Backend-->>User: { status: 'ok' } 200

    User->>FrontendApp: Accesses invalid route
    FrontendApp->>FrontendApp: Catch-all "*" route triggers
    FrontendApp-->>User: Renders NotFound 404 page
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Backend CORS & port configuration: Verify environment variable defaults and fallback logic are secure
  • API base URL integration: Ensure frontend Contact component correctly uses the env variable across build environments
  • Routing additions: Confirm NotFound catch-all route doesn't inadvertently suppress error boundaries or logging
  • SEO files: Validate sitemap structure conforms to XML schema and robots.txt syntax

Possibly related PRs

  • add-contact-form-section #123: Directly related backend/contact.js and Contact form frontend changes—API URL, CORS configuration, and port management are interdependent between both PRs.

Suggested labels

hacktoberfest, hacktoberfest2025, hacktoberfest-accepted

Suggested reviewers

  • oathar

Poem

🐰 A bunny hops down, configuring with care,
.env files set, CORS answered fair,
New routes spring forth, a 404 page so bright,
Sitemaps and robots guide search bots right,
Contact forms connected, the backend's delight! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: improve SEO/a11y, add Contact route, env API URL, 404 page, rob…" is directly and accurately related to the changeset. Each item mentioned in the title corresponds to specific changes present in the PR: SEO/accessibility improvements in index.html with new meta tags and skip-to-content anchor, a new Contact route with associated components and navigation updates, environment-driven API URL configuration in the frontend and backend, a new 404 page with catch-all routing, and robots.txt/sitemap.xml files (indicated by "rob…"). The title is specific and avoids vague terminology, clearly conveying the main improvements across multiple areas (frontend, backend, SEO, accessibility) without being misleading or generic.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/Contact_Form/Contact.jsx (1)

20-21: Remove debug console.log statements before production.

Multiple debug console.log statements are present throughout the form submission handler. These should be removed or replaced with a proper logging solution for production.

Consider:

  • Removing all debug logs (lines 20, 21, 28, 35, 36, 39, 42, 46, 55)
  • Keeping only the console.error on line 50 for error tracking
  • Or using a conditional logging approach:
const log = import.meta.env.DEV ? console.log : () => {};

// Then use log() instead of console.log()
log('Form submission started');

Also applies to: 28-28, 35-36, 39-39, 42-42, 46-46, 55-55

backend/contact.js (1)

33-88: Add rate limiting to prevent abuse.

The /api/contact endpoint has no rate limiting, making it vulnerable to spam and abuse. Consider adding rate limiting middleware.

Install and configure rate limiting:

npm install express-rate-limit

Then apply rate limiting:

 const express = require('express');
 const nodemailer = require('nodemailer');
 const cors = require('cors');
+const rateLimit = require('express-rate-limit');
 require('dotenv').config();

 const app = express();
 const PORT = process.env.PORT || 5002;

 // CORS setup
 const allowedOrigin = process.env.CORS_ORIGIN || 'http://localhost:5173';
 app.use(cors({
   origin: allowedOrigin,
   methods: ['GET', 'POST', 'OPTIONS'],
   allowedHeaders: ['Content-Type'],
 }));

 app.use(express.json());

+// Rate limiter for contact form
+const contactLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 5, // limit each IP to 5 requests per windowMs
+  message: 'Too many contact form submissions, please try again later.',
+});

 // Health check
 app.get('/health', (_req, res) => {
   res.status(200).json({ status: 'ok' });
 });

 // ... transporter setup ...

 // POST /api/contact
-app.post('/api/contact', async (req, res) => {
+app.post('/api/contact', contactLimiter, async (req, res) => {
🧹 Nitpick comments (5)
README.md (2)

74-80: Add guidance on .env file safety and provide cross-platform instructions.

The new frontend environment setup documentation is helpful and well-placed, but the echo command approach has a few gaps:

  1. No mention that .env should be added to .gitignore to prevent accidental commits of sensitive/local config.
  2. The echo command will silently overwrite an existing .env file without warning.
  3. Cross-platform considerations—Windows users may need different instructions.

Consider revising to:

  6. Configure frontend API base URL
     Create a frontend env var so the contact form knows where to send requests:
  
     ```bash
     # create a .env file in the project root (same level as package.json)
+    # (Make sure .env is in .gitignore and never committed)
-    echo "VITE_API_BASE_URL=http://localhost:5002" > .env
+    cat > .env << 'EOF'
+    VITE_API_BASE_URL=http://localhost:5002
+    EOF
     ```

Alternatively, consider creating a .env.example template file in the repo that users can copy:

cp .env.example .env

This approach is more maintainable and makes future env vars discoverable.


74-80: Clarify that frontend .env setup is required before running npm run dev.

The ordering and integration are good, but the documentation could explicitly state that Step 6 is a prerequisite for Step 7. Consider adding a note like:

⚠️ Note: Step 6 must be completed before running the frontend development server, otherwise the contact form will not be able to reach the API.

This prevents users from skipping the configuration step and running into confusing errors.

public/sitemap.xml (1)

3-24: Add optional sitemap metadata for improved SEO signals.

While the sitemap is structurally valid, adding <lastmod> and <changefreq> elements would improve search engine crawl efficiency and freshness signals. Currently, only the root URL includes any metadata (priority).

Consider enhancing entries with:

  • <lastmod>YYYY-MM-DD</lastmod> – helps search engines know when content was last updated
  • <changefreq>weekly|monthly</changefreq> – hints how often each page changes

This is optional but follows sitemap best practices.

Example enhancement for one entry:

  <url>
    <loc>https://osk-open-source.netlify.app/programs</loc>
+   <lastmod>2025-10-29</lastmod>
+   <changefreq>monthly</changefreq>
  </url>
src/components/Contact_Form/Contact.jsx (1)

27-29: Consider moving API base URL outside the handler.

The apiBaseUrl is derived from the environment on every form submission. For efficiency, consider moving it outside the handleSubmit function.

Apply this diff:

+const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5002';
+
 export default function Contact() {
   const [formData, setFormData] = useState({
     name: '',
     email: '',
     subject: '',
     message: ''
   });
   const [status, setStatus] = useState('');
   const [loading, setLoading] = useState(false);

   const handleChange = (e) => setFormData({ ...formData, [e.target.name]: e.target.value });
   
   const handleSubmit = async (e) => {
     e.preventDefault();
     console.log('Form submission started');
     console.log('Form data:', formData);
     
     setLoading(true);
     setStatus('Sending...');
     
     try {
-      const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5002';
       console.log('Sending request to backend...');
       const response = await fetch(`${apiBaseUrl}/api/contact`, {
backend/contact.js (1)

19-22: Consider enhancing the health check endpoint.

The current health check simply returns { status: 'ok' } without verifying dependencies. For production use, consider checking critical services like the email transporter.

Example enhancement:

app.get('/health', async (_req, res) => {
  const health = {
    status: 'ok',
    timestamp: Date.now(),
    uptime: process.uptime(),
  };
  
  // Optional: verify email service
  try {
    await transporter.verify();
    health.emailService = 'ok';
  } catch (error) {
    health.emailService = 'degraded';
    health.status = 'degraded';
  }
  
  const statusCode = health.status === 'ok' ? 200 : 503;
  res.status(statusCode).json(health);
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af122cb and 4f88571.

📒 Files selected for processing (9)
  • README.md (1 hunks)
  • backend/contact.js (1 hunks)
  • index.html (1 hunks)
  • public/robots.txt (1 hunks)
  • public/sitemap.xml (1 hunks)
  • src/App.jsx (3 hunks)
  • src/components/Contact_Form/Contact.jsx (1 hunks)
  • src/components/Navbar.jsx (2 hunks)
  • src/pages/NotFound.jsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/App.jsx (2)
src/components/Contact_Form/Contact.jsx (1)
  • Contact (6-225)
src/pages/NotFound.jsx (1)
  • NotFound (3-19)
🔇 Additional comments (9)
public/robots.txt (1)

4-4: Hardcoded domain in both robots.txt and sitemap.xml limits deployment flexibility; refactor to use environment variables.

Both robots.txt and public/sitemap.xml are hardcoded to https://osk-open-source.netlify.app/. While the project demonstrates environment-aware patterns elsewhere (e.g., VITE_API_BASE_URL in Contact.jsx), the domain configuration is inconsistent. If the site is deployed to staging, preview, or a different domain, both files would become incorrect and degrade SEO effectiveness.

Recommended approach:

  • Add a VITE_PUBLIC_DOMAIN environment variable (e.g., .env)
  • Use build-time substitution to inject the domain into both robots.txt and sitemap.xml during the build process, or
  • Generate sitemap.xml dynamically at build time using the environment variable

This aligns with the project's existing environment configuration pattern.

public/sitemap.xml (1)

1-26: Verify multi-environment deployment strategy before addressing hardcoded domains.

The hardcoded domain issue does exist across three files (robots.txt, sitemap.xml, and index.html). However, the codebase shows no evidence of multi-environment deployment setup—no netlify.toml, .env.example, or staging/dev configuration. While Vite supports environment variables natively (and the project already uses them for backend API URL), whether this concern applies depends on your actual deployment needs.

Confirm: Do you need staging, development, or other non-production deployments? If not, these hardcoded URLs are appropriate for a production-only project. If yes, implement environment-based URL injection for robots.txt and sitemap.xml (and update index.html's og:url and canonical similarly).

index.html (1)

32-32: Excellent accessibility implementation!

The skip-to-content link is properly implemented with screen-reader-only styling and focus visibility. It correctly targets #main-content which exists in src/App.jsx (line 35).

backend/contact.js (1)

7-7: Good environment-driven configuration.

The configurable PORT and CORS_ORIGIN with sensible defaults align well with the frontend changes. The default port (5002) matches the frontend fallback, and the default origin matches Vite's dev server.

If your app will use cookies or authentication, verify whether you need to add the credentials option to CORS:

app.use(cors({
  origin: allowedOrigin,
  methods: ['GET', 'POST', 'OPTIONS'],
  allowedHeaders: ['Content-Type'],
  credentials: true, // if using cookies/auth
}));

Also applies to: 10-10, 12-12

src/components/Navbar.jsx (1)

72-78: LGTM! Consistent navigation implementation.

The Contact link is properly added to both desktop and mobile menus with consistent styling, active state handling, and menu-close behavior matching existing navigation items.

Also applies to: 193-199

src/pages/NotFound.jsx (1)

3-19: Clean and user-friendly 404 page.

The NotFound component provides a clear error message with helpful navigation options. The implementation is simple and effective.

src/App.jsx (3)

16-17: LGTM! Proper imports for new routes.

The Contact and NotFound components are correctly imported and ready for routing.


35-35: Excellent accessibility support.

The id="main-content" attribute properly supports the skip-to-content link added in index.html, enabling keyboard users to bypass navigation.


45-46: Routes are properly configured and dependencies are compatible.

React 19.1.1 is compatible with react-router-dom 7.9.3, as React Router v7 was designed to support both React 18 and React 19. The /contact route and catch-all * route implementation will work correctly under these versions.

<link rel="icon" href="/favicon.png" type="image/png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="canonical" href="https://osk-open-source.netlify.app/" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Canonical URL should be dynamic per route.

The hardcoded canonical URL will incorrectly apply to all routes (/contact, /programs, etc.), causing all pages to declare the homepage as canonical. This harms SEO by preventing proper indexing of individual pages.

Consider using a dynamic solution:

  • For React apps, use a library like react-helmet or react-helmet-async to set canonical URLs per route.
  • Alternatively, handle this at build/deployment time if using SSG/SSR.

Example with react-helmet:

// In each page component
import { Helmet } from 'react-helmet-async';

<Helmet>
  <link rel="canonical" href={`https://osk-open-source.netlify.app${location.pathname}`} />
</Helmet>
🤖 Prompt for AI Agents
In index.html around line 27 the canonical link is hardcoded to the homepage
which makes every route report the same canonical URL; remove this static tag
from the global HTML and instead generate a per-route canonical URL at runtime
or build time — for a React app add canonical tags in each page component (e.g.,
via react-helmet or react-helmet-async) using the current pathname to build the
absolute URL, or if using SSG/SSR emit the canonical during page generation;
ensure the canonical is the full absolute URL and handle trailing slashes/params
consistently.

@oathar
Copy link
Copy Markdown
Contributor

oathar commented Oct 29, 2025

Please attach a screen recording of the website working

@MananGilhotra
Copy link
Copy Markdown
Author

MananGilhotra commented Oct 29, 2025 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants