← My work

Full-stack nonprofit website · Headless CMS

ACSFI

ACSFI gives the Astro Charitable and Scholarship Foundation a public website for its mission, programs, activity reports, upcoming events, and contact information, backed by a CMS for ongoing updates.

Contribution
Designed and built the public website and CMS
Current state
Live organization website · Source available
Built with
Next.js · TypeScript · Strapi · MySQL · Cloudinary · Turborepo · Bun · Docker

Product showcase

Inside ACSFI

Selected product surfaces from the live experience, presented without simulated chrome or scroll-driven effects.

ACSFI homepage with a community outreach photograph and the message Empowering communities, inspiring change
01Foundation homepageFull resolution ↗
ACSFI programs page introducing charitable, educational, and social welfare programs
02Programs directoryFull resolution ↗
ACSFI activity report showing the title, date, venue, photograph, and objectives of a relief activity
03Activity reportFull resolution ↗

Problem and response

The problem

The foundation needed one public source for its programs and community work. New activity reports, event details, statistics, and media also needed to be published without editing the website source for every update.

The approach

The website presents the foundation's mission, programs, stories, and contact details through a responsive Next.js frontend. A separate Strapi CMS stores hero media, programs, activities, statistics, quotes, and upcoming events for the public pages to retrieve through a typed API client.

Architecture

A Bun and Turborepo monorepo contains a typed Next.js frontend and a Strapi backend. The frontend retrieves CMS content through authenticated server-side requests, while MySQL stores production content and Cloudinary serves managed media.

  1. Next.js public frontend

    The Pages Router provides the homepage, About, Programs, Stories, individual activity reports, Contact, and a custom not-found page. Static generation and incremental regeneration keep public pages fast to serve.

  2. Typed content boundary

    TypeScript models mirror Strapi entities for activities, programs, media, statistics, quotes, and upcoming events. One server-only fetch wrapper centralizes the API URL, bearer token, and response handling.

  3. Strapi content service

    Strapi collection and single types give administrators structured fields and program relationships. MySQL stores production records and Cloudinary handles uploaded images and files.

  4. Monorepo delivery

    Bun installs the workspaces, Turborepo coordinates tasks, Biome and Lefthook check staged code, and separate Docker configurations support the frontend and CMS runtimes.

Key product and engineering decisions

These decisions affect what users can do, where product rules live, and how the system handles failure or change.

Separate presentation from content management

The public website and Strapi administration run as separate applications. Foundation content can change through the CMS while the frontend keeps control of layout, interaction, and accessibility.

Model activities as structured reports

Each activity can store its title, date, venue, participants, objectives, purpose, narrative, authors, program relation, featured state, and media instead of reducing the work to a generic blog post.

Pre-render public content

Core pages are statically generated from the CMS. The stories index uses incremental regeneration so published activity reports can appear without rebuilding every request on the server.

Keep deployments independent

Turborepo manages the frontend and CMS in one repository, while Vercel, Docker, and the production API configuration allow each application to be built and deployed for its own runtime needs.

What you can inspect

The live product and public repository provide concrete evidence of the implemented features, architecture, tests, and documentation.