Interactive storytelling platform for The New Humanitarian (TNH). This codebase powers data-driven visualizations, embeddable dashboards, and immersive editorial projects.
π Production: interactive.thenewhumanitarian.org
π§ͺ Preview/Staging: preview.thenewhumanitarian.org
This SvelteKit application serves as TNH's platform for:
| Category | Technology | Version |
|---|---|---|
| Framework | SvelteKit | 2.x |
| UI | Svelte | 5.x |
| Bundler | Vite | 7.x |
| Styling | Tailwind CSS | 4.x (CSS-first via @tailwindcss/vite) |
| Maps | Mapbox GL JS | 3.x |
| Charts/Data | D3.js | 3.x / 4.x |
| Animations | GSAP | 3.15 |
| CMS | Storyblok (@storyblok/svelte) |
4.x |
| Component docs | Storybook | 10.x |
| Linting | ESLint + Prettier | 10.x / 3.x |
| Hosting | Vercel (@sveltejs/adapter-vercel) |
Node 24.x |
| Runtime | Node.js LTS | 24.x (see .nvmrc) |
| Package manager | pnpm via Corepack | 11.x (pinned in package.json) |
nvm install 24).env β create in project root with required tokens (see Environment Variables)# 1. Clone the repository
git clone <repository-url>
cd tnh-storytelling-sveltekit
# 2. Use Node 24 LTS (.nvmrc => 24)
nvm install # first time only
nvm use
# 3. Enable Corepack and activate the pinned pnpm version (packageManager in package.json => [email protected])
corepack enable
corepack prepare [email protected] --activate
# 4. Install dependencies
pnpm install
# 5. Generate local SSL certificates (see HTTPS Setup below β each developer runs this once)
# 6. (Optional) Cache Gaza map data for local dev β file is gitignored; Vercel builds run this automatically
pnpm pre-fetch-gaza
# 7. Start the development server
pnpm dev
The dev server runs at https://localhost:5173 with HTTPS enabled. If port 5173 is in use, Vite picks the next available port (e.g. 5174).
The dev server runs over HTTPS because the Storyblok Visual Editor requires a secure connection to load your local preview. Without HTTPS, the Visual Editor iframe is blocked by the browser.
Certificates are per-developer β they are signed by your local mkcert CA and are gitignored (cert/*.pem). Regenerate them on your machine (see cert/README.md).
One-time setup:
# macOS
brew install mkcert
mkcert -install # adds mkcert's local CA to your system trust store
# Generate certs (from project root)
cd cert
mkcert -key-file localhost-key.pem -cert-file localhost.pem localhost 127.0.0.1 ::1
cd ..
Vite reads these files from vite.config.ts:
cert/localhost.pem β certificatecert/localhost-key.pem β private keyTroubleshooting
| Symptom | Fix |
|---|---|
Browser shows "Not Secure" with a red strikethrough on https |
Certs were likely generated on another machine. Re-run the mkcert commands above on your Mac, restart pnpm dev, hard-refresh the browser. |
| Storyblok Visual Editor won't load localhost | Confirm you're using https:// (not http://) and that mkcert CA is installed (mkcert -install). |
| Certificate expired | Re-run mkcert in cert/ β new certs are valid for ~2 years. |
Note: HTTPS is only required for local dev. Production and staging on Vercel use Vercel's own TLS β no mkcert needed there.
Create a .env file in the project root:
# Public (exposed to client)
PUBLIC_BASE_URL=https://interactive.thenewhumanitarian.org
PUBLIC_MAPBOX_TOKEN=pk.your_mapbox_public_token
PUBLIC_GA4_ID=G-XXXXXXXXXX
# Private (server-side only)
GA4_API_SECRET=your-ga4-api-secret
tnh-storytelling-sveltekit/
βββ src/
β βββ lib/
β β βββ assets/ # Project-specific images & media
β β βββ components/ # Reusable Svelte components
β β β βββ animations/ # Animation utilities
β β β βββ gaza-map/ # Gaza dashboard components
β β β βββ icons/ # Logo, share icons
β β β βββ projects/ # Project-specific components
β β β β βββ LebanonDisplaced/
β β β β βββ SyriaMap/
β β β βββ vendor/ # Third-party libraries (TimelineJS)
β β βββ data/ # Cached data (JSON, GeoJSON)
β β βββ stores/ # Svelte stores
β β βββ utils/ # Helper functions
β βββ routes/ # SvelteKit routes
β β βββ api/ # API endpoints
β β βββ dashboard/ # Admin dashboard
β β βββ embeddable/ # Embeddable widgets
β β β βββ map/ # Map projects (Gaza, Syria)
β β β βββ sharepoint/ # Social embeds
β β β βββ timeline-x/ # Timeline embeds
β β βββ login/ # Authentication
β β βββ stories/ # Long-form narratives
β βββ stories/ # Storybook stories
βββ static/ # Static assets & legacy projects
β βββ scripts/ # Embeddable scripts
βββ scripts/ # Build-time data fetching
βββ cert/ # Local mkcert SSL certs (regenerate per developer β see HTTPS Setup)
βββ pnpm-lock.yaml # Lockfile (commit changes; use --frozen-lockfile on CI/Vercel)
βββ .nvmrc # Node version pin (24)
| Command | Description |
|---|---|
pnpm dev |
Start development server with HTTPS |
pnpm build |
Pre-fetch data and build for production |
pnpm preview |
Preview production build locally |
pnpm install --frozen-lockfile |
CI/Vercel-style install (no lockfile changes) |
pnpm pre-fetch-data |
Fetch all project data (orchestrator) |
pnpm pre-fetch-gaza |
Fetch Gaza map data only |
pnpm check |
Run Svelte type checking |
pnpm lint |
Run ESLint and Prettier |
pnpm format |
Format code with Prettier |
pnpm storybook |
Launch Storybook on port 6006 |
pnpm build-storybook |
Build static Storybook |
Route: /embeddable/map/2025-09/gaza
Live: interactive.thenewhumanitarian.org/embeddable/map/2025-09/gaza
Interactive map and timeline showing incidents involving aid seekers in Gaza. Features:
Route: /stories/2025/05/22/lebanon-displacement-diaries
Live: interactive.thenewhumanitarian.org/stories/2025/05/22/lebanon-displacement-diaries
Immersive long-form narrative with:
/ar)Route: /embeddable/map/2024-11/syria
Live: interactive.thenewhumanitarian.org/embeddable/map/2024-11/syria
Interactive map of Syria with populated places data.
Route: /embeddable/timeline-x/[sheetId]
Dynamic timelines powered by Knight Lab's TimelineJS, configured via Google Sheets.
This codebase consolidates projects from a previous Gatsby JS repository. Legacy static assets (HTML, CSS, JS, images) are hosted in the /static folder and served directly by Vercel.
These include older interactive pieces that were migrated to preserve their functionality without requiring a full rewrite. They are accessible via their original paths under the static directory.
The project is hosted on Vercel with automatic deployments:
| Branch | Environment | URL |
|---|---|---|
main |
Production | interactive.thenewhumanitarian.org |
preview |
Staging | preview.thenewhumanitarian.org |
| Feature branches | Preview | Auto-generated Vercel preview URLs |
Configure these in the Vercel dashboard (Project β Settings β General / Build):
| Setting | Value |
|---|---|
| Node.js Version | 24.x |
| Install Command | pnpm install --frozen-lockfile |
| Build Command | pnpm build (default) |
Corepack reads the pinned pnpm version from packageManager in package.json ([email protected]).
main or previewpnpm install --frozen-lockfile on Node 24pre-fetch-data.js to cache external data@sveltejs/adapter-vercelThe project uses a build-time data fetching system to cache external data sources.
See scripts/README.md for detailed documentation on:
Projects can be embedded on external websites using a simple script tag:
<!-- Gaza Dashboard Embed -->
<div id="gaza-aid-killings"></div>
<script
src="https://interactive.thenewhumanitarian.org/embeddable/map/2025-09/gaza/embed"
defer
></script>
The embed script:
<div id="my-custom-id"></div>
<script
src="https://interactive.thenewhumanitarian.org/embeddable/map/2025-09/gaza/embed"
data-target="my-custom-id"
data-src="https://custom-source-url"
defer
></script>
Component development and documentation via Storybook:
pnpm storybook
Opens at http://localhost:6006
| Project | URL |
|---|---|
| Gaza Aid Seekers Dashboard | /embeddable/map/2025-09/gaza |
| Gaza Spotlight Counter | /embeddable/map/2025-09/gaza/spotlight |
| Lebanon Displacement Diaries (EN) | /stories/.../lebanon-displacement-diaries/home |
| Lebanon Displacement Diaries (AR) | /stories/.../lebanon-displacement-diaries/ar/home |
| Project | URL |
|---|---|
| Year in Photos 2024 | /stories/2024/12/27/year-in-photos |
| Drawing Derna | /stories/2023/11/28/art-time-crisis-drawing-derna |
| WhatsApp Lebanon | /stories/2022/07/28/whatsapp-lebanon |
| DariΓ©n Gap Migration | /stories/2022/05/10/us-asylum-darien-gap... |
| Rohingya Camp Women | /stories/2021/12/21/bangladesh-rohinyga... |
| Drawing Syria's Trauma | /stories/2021/12/8/drawing-syrias-trauma |
| Mediterranean Migration | /stories/2021/11/17/mediterranean-migration-europe |
| A Decade of War in Syria | /stories/2021/a-decade-of-war-in-syria |
| Bangladesh Cyclone Amphan | /stories/2020/bangladesh-amphan... |
| Report | URL |
|---|---|
| Annual Report 2023 | /reports/2024/07/25/annual-report-2023 |
| Annual Report 2022 | /reports/2023/06/27/annual-report-2022 |
| Our Strategy | /reports/2022/12/05/our-strategy |
| Annual Report 2021 | /reports/2022/06/27/annual-report-2021 |
nvm use β ensure Node 24 is activepreviewpnpm install && pnpm dev β test locally (regenerate mkcert certs if needed)preview for staging (preview.thenewhumanitarian.org)main for production (release candidate after staging is verified)Β© The New Humanitarian. All rights reserved.