Bay Area IT — Website
Marketing website for Bay Area IT, an IT service provider based in Corpus Christi, TX. Built with React 19, Vite 6, TypeScript, and Tailwind CSS. Deployed as a static site served by Caddy inside Docker.
Table of Contents
- Tech Stack
- Project Structure
- Local Development
- Build & Preview
- Deployment (Docker + Caddy)
- Adding Content
- Scripts Reference
Tech Stack
| Layer | Technology |
|---|---|
| UI Framework | React 19 |
| Build Tool | Vite 6 |
| Language | TypeScript 5 |
| Styling | Tailwind CSS 3 |
| Routing | React Router 7 |
| Animations | Framer Motion 12, GSAP 3 |
| Smooth Scroll | Lenis |
| Web Server | Caddy 2 (inside Docker) |
| Containerization | Docker + Docker Compose |
Project Structure
bayarea/
├── components/ # Shared UI components
│ ├── Navbar.tsx
│ ├── Footer.tsx
│ ├── Hero.tsx
│ ├── Services.tsx
│ ├── Blog.tsx
│ ├── CTA.tsx
│ ├── Testimonials.tsx
│ ├── Process.tsx
│ ├── AreasWeServe.tsx
│ ├── SEO.tsx # Head/meta tag injection
│ ├── Breadcrumb.tsx
│ └── LoadingScreen.tsx
├── src/
│ ├── pages/ # Route-level page components
│ │ ├── HomePage.tsx
│ │ ├── AboutPage.tsx
│ │ ├── ServicesPage.tsx
│ │ ├── ServicePage.tsx # Dynamic: /services/:slug
│ │ ├── BlogPage.tsx
│ │ ├── BlogPostPage.tsx # Dynamic: /blog/:slug
│ │ ├── LocationPage.tsx # Dynamic: /locations/:slug
│ │ ├── LocationsPage.tsx
│ │ ├── ContactPage.tsx
│ │ ├── PrivacyPolicyPage.tsx
│ │ ├── TermsOfServicePage.tsx
│ │ └── LegalPage.tsx
│ ├── data/
│ │ └── seoData.ts # All location, service, and blog post data
│ ├── routes/ # Route definitions
│ └── index.css # Global styles
├── scripts/
│ ├── prerender-routes.ts # Generates per-route index.html files
│ ├── prune-dist-assets.mjs # Removes unused assets from dist/
│ ├── generate-sitemap.ts # Generates public/sitemap.xml
│ ├── generate-robots.ts # Generates public/robots.txt
│ └── optimize-images.mjs # Converts images to WebP
├── public/
│ ├── assets/services/ # Service page images (.webp)
│ ├── images/blog/ # Blog post images (.webp)
│ ├── sitemap.xml
│ └── robots.txt
├── Dockerfile # Multi-stage: Node build → Caddy serve
├── docker-compose.yml # Runs the container on localhost:8080
├── Caddyfile # Caddy config inside the container
├── caddy-host.snippet # Paste this into your host Caddy config
├── tailwind.config.cjs
├── postcss.config.cjs
├── vite.config.ts
└── tsconfig.json
Local Development
Prerequisites
- Node.js 22+ (or use nvm)
- npm
Setup
# Install dependencies
npm install
# Start the dev server
npm run dev
The app runs at http://localhost:3012
Hot Module Replacement (HMR) is enabled. If port 3012 is taken, Vite picks the next available port automatically.
Build & Preview
# Production build (Vite + prerender + asset pruning)
npm run build
# Preview the production build locally
npm run preview
The build pipeline runs three steps in sequence:
vite build— bundles and outputs todist/prune-dist-assets.mjs— removes any unreferenced files fromdist/assets/prerender-routes.ts— writes a uniqueindex.htmlper route intodist/with correct<title>,<meta>, canonical URLs, Open Graph tags, and JSON-LD schema
The result is a fully static site where every URL has its own HTML file — no server-side rendering required.
Deployment (Docker + Caddy)
This project uses a two-Caddy setup:
Internet
└── Host Caddy (HTTPS, port 443)
└── Docker container (internal port 80 → host localhost:8080)
└── Caddy inside container
└── /srv (built static files)
Step 1 — Build and start the container
On your server, copy the project files (or clone the repo), then run:
docker compose up -d --build
This builds the image (Node 22 compiles the app, Caddy serves it) and starts the container. The app is now available at http://localhost:8080 — only reachable from the server itself.
To verify:
curl -I http://localhost:8080
# HTTP/1.1 200 OK
Step 2 — Configure the host Caddy
Add the following to your host Caddy config (usually /etc/caddy/Caddyfile).
A ready-to-paste version is in caddy-host.snippet:
bayareait.services, www.bayareait.services {
encode zstd gzip
reverse_proxy localhost:8080
@www host www.bayareait.services
redir @www https://bayareait.services{uri} permanent
}
Replace bayareait.services with your actual domain.
Step 3 — Reload host Caddy
sudo systemctl reload caddy
# or
caddy reload --config /etc/caddy/Caddyfile
Caddy automatically obtains and renews an SSL certificate from Let's Encrypt — no manual certificate management needed.
Updating the site
# Pull latest changes, rebuild, restart
git pull
docker compose up -d --build
Zero-downtime: Docker Compose replaces the old container while the new one starts.
Useful Docker commands
# View logs
docker compose logs -f
# Stop the container
docker compose down
# Rebuild without cache
docker compose build --no-cache
docker compose up -d
Adding Content
All page content is driven by src/data/seoData.ts. No new page files needed for standard locations, services, or blog posts — just add a new entry to the right array.
Add a location page
// src/data/seoData.ts → locationData array
{
city: "Sinton",
slug: "locations/it-support-sinton",
title: "IT Support Sinton, TX | Bay Area IT",
description: "...",
h1: "IT Support for Businesses in Sinton, TX",
keywords: ["IT support Sinton", "..."],
content: `<p>...</p>`,
faq: [
{ question: "...", answer: "..." }
]
}
Add a service page
// src/data/seoData.ts → serviceData array
{
id: "9",
slug: "services/cloud-backup",
title: "Cloud Backup Services | Bay Area IT",
description: "...",
h1: "Cloud Backup for Corpus Christi Businesses",
keywords: ["cloud backup", "..."],
content: `<p>...</p>`,
faq: []
}
Add a blog post
// src/data/seoData.ts → blogPostData array
{
slug: "blog/your-post-slug",
title: "Your Post Title | Bay Area IT",
description: "...",
h1: "Your Post Heading",
keywords: ["..."],
content: `<p>...</p>`,
date: "2026-03-25",
image: "/images/blog/your-image.webp"
}
After adding content, rebuild:
npm run build
# or on the server:
docker compose up -d --build
Scripts Reference
| Script | Command | Description |
|---|---|---|
| Dev server | npm run dev |
Starts Vite dev server on port 3012 |
| Production build | npm run build |
Vite build + asset pruning + prerendering |
| Preview build | npm run preview |
Serves dist/ locally via Vite |
| Generate sitemap | npm run generate:seo |
Writes public/sitemap.xml and public/robots.txt |
| Optimize images | npm run optimize:images |
Converts images in public/ to WebP |
| Prerender only | npm run prerender:routes |
Re-runs route prerendering on existing dist/ |