Project Overview
This repository delivers a minimal React + TypeScript content site scaffold powered by Vite. It includes essential pages, development tooling, and linting presets to help you build or customize an ImpactSOS-themed blog or informational site.
Purpose
Provide a lightweight, extensible starter for ImpactSOS content sites:
- Bootstraps a React + TS app with Vite for fast builds
- Ships Home and About templates aligned with ImpactSOS branding
- Offers plugin-driven Fast Refresh and ESLint setups
Key Features
- Vite + React + TypeScript: Zero-config build, HMR, and production optimizations
- Fast Refresh: Instant component reloads during development
- ESLint Integration: Extendable, type-aware linting with
@typescript-eslint
and React plugins - Prebuilt Pages:
Home.tsx
: Main landing page with styled headerabout/About.tsx
: Mission, impact metrics, and team profiles
When to Choose This Codebase
Use this starter if you need to:
- Launch an ImpactSOS or nonprofit microsite with minimal setup
- Prototype content-driven pages and team showcases
- Integrate custom layouts or data sources on top of a solid React + TS foundation
Getting Started
1. Install & Run
git clone https://github.com/Just-Lend/impact-blog.git
cd impact-blog
npm install
npm run dev # starts Vite dev server on localhost:3000
2. Enable Fast Refresh
vite.config.ts:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' // includes Fast Refresh
export default defineConfig({
plugins: [react()],
})
3. Extend ESLint for Production
.eslintrc.js:
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended', // TS rules
'plugin:react/recommended', // React-specific rules
],
plugins: ['@typescript-eslint', 'react'],
settings: { react: { version: 'detect' } },
}
4. Customize Pages
Home.tsx (src/pages/Home.tsx):
import React from 'react'
import './Home.css'
export default function Home() {
return (
<main className="home-container">
<h1>Welcome to ImpactSOS Blog</h1>
<p>Share stories, metrics, and achievements here.</p>
{/* Add featured posts or components */}
</main>
)
}
About.tsx (src/pages/about/About.tsx):
import React from 'react'
import teamData from './team.json'
export default function About() {
return (
<section className="about-page">
<h2>Our Mission</h2>
<p>Empowering communities through data-driven impact.</p>
<div className="team-grid">
{teamData.map(member => (
<article key={member.id}>
<img src={member.avatar} alt={member.name} />
<h3>{member.name}</h3>
<p>{member.role}</p>
</article>
))}
</div>
</section>
)
}
Use these examples to tailor the site’s look, fetch live data, or integrate CMS feeds.
Getting Started
Fast-track to a running local instance: install dependencies, launch the dev server, and verify your first success screen.
Prerequisites
• Node.js >= 16
• npm >= 8 or Yarn >= 1.22
• Git
Installation
- Clone the repository
git clone https://github.com/Just-Lend/impact-blog.git cd impact-blog
- Install dependencies
npm install # or yarn
Start the Development Server
Launch Vite’s hot-reload server:
npm run dev
# or
yarn dev
By default, Vite serves on http://localhost:5173. Open that URL to view your app. The server reloads instantly on file changes.
Verify Your First Success Screen
After npm run dev
:
- Browser tab title reads ImpactSOS Zines (from
public/index.html
). - The DOM contains
<div id="root"></div>
. - No errors appear in the console.
If you have a minimal AppContainer
rendering (e.g., a “Hello, world!”), you’ll see it in the viewport. This confirms React, TypeScript, TailwindCSS and Vite are configured correctly.
Build and Preview (Optional)
Create a production build
npm run build # or yarn build
Outputs optimized files to
dist/
.Preview the production build locally
npm run preview # or yarn preview
Open http://localhost:4173 (default) to smoke-test your built assets.
With these steps you have a fully operational local environment ready for development and testing.
Architecture & Project Structure
This section outlines the codebase organization, client-side routing setup, and the flow of data from API calls to rendered pages.
Project Directory Layout
src/
┣ api/
┃ ┣ campaignArticleService.ts
┃ ┗ otherService.ts
┣ components/
┃ ┣ alerts/
┃ ┣ Header.tsx
┃ ┣ Footer.tsx
┃ ┣ ScrollToTop.tsx
┃ ┗ LayoutContainer.tsx
┣ pages/
┃ ┣ Magazine.tsx
┃ ┣ Article.tsx
┃ ┣ CategorisedArticles.tsx
┃ ┣ UNSDGArticles.tsx
┃ ┣ LocalAuthorityArticles.tsx
┃ ┣ about/
┃ ┃ ┗ About.tsx
┃ ┣ RegisterInterest.tsx
┃ ┗ …
┣ App.tsx
┣ AppContainer.tsx
┗ index.tsx
Entry Point: src/index.tsx
Mounts the application within a full-screen container.
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import AppContainer from './AppContainer';
const root = ReactDOM.createRoot(
document.getElementById('root')!
);
root.render(
<AppContainer>
<App />
</AppContainer>
);
Layout Wrapper: src/AppContainer.tsx
Provides a full-viewport flex layout for header, content, and footer.
import styled from 'styled-components';
const AppContainer = styled.div`
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100vw;
background-color: #fff;
`;
export default AppContainer;
Routing Configuration: src/App.tsx
Defines client-side routes and global layout components.
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import ScrollToTop from './components/ScrollToTop';
import Header from './components/Header';
import LayoutContainer from './components/LayoutContainer';
import Footer from './components/Footer';
// Page imports
import Magazine from './pages/Magazine';
import ArticlePage from './pages/Article';
// …other page imports
const App: React.FC = () => (
<Router>
<ScrollToTop />
<Header />
<LayoutContainer>
<Routes>
<Route path="/" element={<Magazine />} />
<Route path="/articles/:slug" element={<ArticlePage />} />
<Route path="/categories/:id" element={<CategorisedArticles />} />
<Route path="/unsdgs/:unsdgNumber" element={<UNSDGArticles />} />
<Route path="/local-authorities/:laId" element={<LocalAuthorityArticles />} />
<Route path="/about" element={<About />} />
<Route path="/register-interest" element={<RegisterInterest />} />
<Route path="/support" element={<Support />} />
{/* …product and static routes */}
</Routes>
</LayoutContainer>
<Footer />
</Router>
);
export default App;
Key Points
- Wrap the app in
<BrowserRouter>
once, at the root. - Place
<Header>
and<Footer>
outside<Routes>
to persist across pages. - Use
<LayoutContainer>
to constrain content width and add padding. - Use
<ScrollToTop>
to reset scroll on navigation.
Data Flow: API → Page → UI
Route Activation
React Router matches URL (e.g./articles/:slug
) and mounts the corresponding page component.Parameter Retrieval
The page usesuseParams()
to extract dynamic segments (slug
,id
, etc.).Data Fetching
InuseEffect
, the page calls a service function fromsrc/api/...
.
Example for article details:import React, { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { getCampaignArticleBySlug } from '@/api/campaignArticleService'; import ArticleDetail from '../components/ArticleDetail'; const ArticlePage: React.FC = () => { const { slug } = useParams<{ slug: string }>(); const [article, setArticle] = useState<Article | null>(null); useEffect(() => { if (slug) { getCampaignArticleBySlug(slug) .then(setArticle) .catch(console.error); } }, [slug]); if (!article) return <p>Loading...</p>; return <ArticleDetail article={article} />; }; export default ArticlePage;
Service Layer
Service functions insrc/api/…
use Axios withimport.meta.env.VITE_API_URL
as base URL. They return typed data (e.g.,Article[]
orArticle | null
).State & Rendering
The page sets state with fetched data and renders UI components (e.g.,ArticleDetail
,ArticleList
).Consistent Layout
Header, footer, and layout container remain static, isolating pages to the central<LayoutContainer>
.
Use this structure when adding new pages or API integrations: define a route in App.tsx
, create a page component under src/pages
, fetch data via a service in src/api
, and compose UI within the existing layout.
Configuration & Environment
Centralize all application-wide settings: deployment commands, environment variables, Tailwind breakpoints, TypeScript compilation, and Vite build options.
1. Deployment Process (Procfile)
Define the web process for Heroku or any platform using a Procfile
at project root:
web: npm start
Ensure your package.json
has a matching start
script:
// package.json
{
"scripts": {
"start": "vite preview --port $PORT"
}
}
• During deploy, Heroku reads web
and runs npm start
.
• VITE_PORT and other $ENV
vars propagate automatically.
2. Type-Safe Environment Variables
a. Define Vite globals (src/vite-env.d.ts)
Add at src/vite-env.d.ts
:
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_DEBUG_MODE?: 'on' | 'off'
// add more VITE_ vars here
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
b. Add custom .env
files
Create .env.development
and .env.production
:
VITE_API_URL=https://api.dev.example.com
VITE_DEBUG_MODE=on
Vite merges and exposes only VITE_
prefixed vars via import.meta.env
.
c. Access variables in code
// src/utils/api.ts
const apiUrl = import.meta.env.VITE_API_URL
export async function fetchStatus() {
const res = await fetch(`${apiUrl}/status`)
return res.json()
}
• Restart npm run dev
after updating .env
or vite-env.d.ts
.
3. TailwindCSS Breakpoints & Fonts
a. Extend tailwind.config.js
// tailwind.config.js
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
fontFamily: {
hanken: ["Hanken Grotesk", "sans-serif"],
},
},
screens: {
monitor: { max: "2560px" },
desktop: { max: "1279px" },
tablet: { max: "1199px" },
mobile: { max: "820px" },
// To add new breakpoint:
// small: { max: "600px" }
},
},
plugins: [],
}
b. Import base styles
/* src/index.css */
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
body {
font-family: "Hanken Grotesk", sans-serif;
margin: 0;
}
c. Usage examples
// src/components/Header.tsx
export function Header() {
return (
<header className="font-hanken text-5xl tablet:text-3xl mobile:text-2xl p-8 tablet:p-4">
<h1>Impact Blog</h1>
</header>
)
}
• tablet:text-3xl
applies at ≤1199px, mobile:text-2xl
at ≤820px.
• Add new screens by updating screens
and restarting the dev server.
4. TypeScript Configuration
a. Project references (tsconfig.json)
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
b. App settings (tsconfig.app.json)
{
"compilerOptions": {
"target": "ES2020",
"jsx": "react-jsx",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"strict": true,
"noEmit": true
},
"include": ["src"]
}
c. Node/Vite config type-check (tsconfig.node.json)
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true
},
"include": ["vite.config.ts"]
}
• Add new paths: add "paths"
under compilerOptions
in tsconfig.app.json
.
• Run type-checks: npx tsc --build tsconfig.app.json tsconfig.node.json
.
5. Vite Build & Plugin Configuration
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig(({ mode }) => ({
plugins: [
react(),
tailwindcss(),
// e.g., svgLoader(), // add safely here
],
resolve: {
alias: { '@': '/src' }
},
server: {
port: Number(process.env.PORT) || 3000,
open: true
},
define: {
// expose runtime flags
__DEV__: mode === 'development'
}
}))
• Conditional plugins: wrap based on mode
.
• Aliases: align with TS paths
.
• Server port: uses $PORT
from Procfile in production.
With this setup, you manage deployment commands, type-safe environment variables, custom Tailwind breakpoints, strict TypeScript settings, and a flexible Vite build pipeline—all in sync.
Development & Contributing
This section describes how to set up your local environment, enforce code quality, follow branch and commit conventions, and prepare for testing.
Prerequisites
- Node.js ≥ 14
- npm ≥ 6
Setup
Install dependencies and start the dev server:
# Install all dependencies
npm install
# Start Vite dev server with React Fast Refresh
npm run dev
Available Scripts
Run these from the project root:
# Build production assets into dist/
npm run build
# Preview the production build on localhost
npm run preview
# Lint all .ts/.tsx files
npm run lint
# Serve the static build (you need a simple HTTP server)
npm run start
Linting & Formatting
We use ESLint (configured in eslint.config.js
) to enforce TypeScript and React best practices.
Run lint checks:
npm run lint
Auto-fix lint errors:
npm run lint -- --fix
Key ESLint rule for Fast Refresh compatibility:
// eslint.config.js
import reactRefresh from 'eslint-plugin-react-refresh'
export default {
plugins: { 'react-refresh': reactRefresh },
rules: {
// Warn if you export non-components and break Fast Refresh
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
}
To enable type-aware rules, ensure you have:
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript
And in your ESLint config:
export default {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'react', 'react-refresh'],
// …other settings…
}
Branching Strategy
Use descriptive, prefixed branch names off main
:
feature/<feature-name>
– new featurefix/<bug-fix>
– bug fixeschore/<task>
– maintenance, tooling, docsrelease/<version>
– release preparation
Always rebase or merge from main
before creating a PR.
Commit & Pull Request Guidelines
Follow Conventional Commits:
<type>(<scope>): <short description>
Common types:
feat
: a new featurefix
: a bug fixchore
: build or tooling changesdocs
: documentation only changes
Example:
feat(ui): add responsive sidebar toggle
When opening a PR against main
:
- Link any related issue (e.g. “Closes #42”).
- Describe what and why, include screenshots if UI changes.
- Ensure
npm run lint
passes without errors.
Testing (Placeholder)
We plan to integrate a testing framework (e.g. Vitest or Jest) and React Testing Library:
- Unit tests for components
- Integration tests for pages and API interactions
- CI workflow to run tests on each PR
Contributions adding tests are encouraged under a new __tests__/
folder.
Deployment & Hosting
This section covers building a production-ready image of your React + TailwindCSS app, configuring environment variables, and deploying on Heroku or any platform that runs npm start
.
1. Build Production Assets
Generate optimized static files in the /dist
folder:
# Install dependencies and build
npm ci
npm run build
tsc -b
compiles TypeScript.vite build
outputs minified assets todist/
.
2. Configure Environment Variables
Vite exposes only variables prefixed with VITE_
at build time.
- Create
.env.production
in project root:VITE_API_URL=https://api.example.com VITE_BASE_PATH=/
- Access in code:
// src/config.ts export const API_URL = import.meta.env.VITE_API_URL;
- Override on host platform:
# Heroku example heroku config:set VITE_API_URL=https://api.prod.com
Base Path Configuration (vite.config.ts)
If you serve under a subpath, set base
:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
base: process.env.VITE_BASE_PATH ?? '/',
plugins: [react()],
});
3. Procfile & Runtime Server
Heroku (and similar) detect a Procfile
at root:
Procfile
web: npm start
npm start
runsserve -s dist
—a static file server.- Ensure
serve
is in dependencies:"dependencies": { "serve": "^14.0.1", // ... }
4. Deploy to Heroku
- Login and create app:
heroku login heroku create impact-blog
- Push and release:
git push heroku main
- Set production env vars:
heroku config:set VITE_API_URL=https://api.example.com
- Open in browser:
heroku open
Heroku will automatically:
- Install deps (
npm ci
) - Detect Procfile → run
npm start
- Serve
/dist
5. Deploy to Other Platforms
Any host that runs npm start
works:
Netlify
- Connect Git repo
- Build command:
npm ci && npm run build
- Publish directory:
dist
- Add
VITE_
env vars in Netlify Dashboard
Vercel
- Import project
- Set Framework Preset: “Other”
- Build command:
npm run build
- Output directory:
dist
- Define env vars under Settings → Environment Variables
Docker (optional)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
RUN npm install -g serve
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["serve", "-s", "dist", "-l", "3000"]
Build & run:
docker build -t impact-blog .
docker run -p 3000:3000 -e VITE_API_URL=https://api.example.com impact-blog