FLASH is a privacy-focused web application designed to capture and share memories from weddings and other special events. Create events, let guests join and upload photos, then moderate the images - choosing which images gets rejected and which are approved. A key feature of the application is the slideshow, so you can shows all approved images on the big screen.
| Creating an Event | Upload Images | Moderation |
|---|---|---|
![]() |
![]() |
![]() |
- Frontend: React with TypeScript + vanilla CSS
- Framework: Next.js (handles both frontend rendering and API routes)
- Backend: Node.js (via Next.js API routes)
- PWA support: The app can be installed and used as a Progressive Web App
- Storage: Configurable through the file-storage package (local by default, optional cloud backends)
- Testing/infrastructure: Docker is used to run local instances of external services (e.g. storage) during testing
This project uses a monorepo structure where the main application and shared functionality are split into separate packages:
| Package | Description |
|---|---|
website |
The heart of FLASH. Built with Next.js, it contains both the React frontend and backend API routes. The frontend uses shared components from UI and styling from tokens. The backend provides endpoints for events, image handling, authentication, and admin functionality. |
file-storage |
Handles all file storage operations through a common interface (upload, retrieval, deletion). The application interacts with storage exclusively through this package. Uses local filesystem storage by default, with support for alternative backends configured via environment variables. |
UI |
Shared React component library used by the frontend. Components are intended to be reusable and consistent across the application. |
tokens |
Centralized TypeScript-first CSS design tokens (e.g. colors, spacing, typography) used by the UI components and frontend. |
FLASH can be self-hosted on any VPS capable of running Node.js. For ease of use
we provide a Dockerfile which packages all the dependencies needed for FLASH
to function. For building and running the application locally, see
Running the Application.
Currently we do not provide pre-built docker images, so you will have to build
one yourself. The only requirement is that your computer has Docker installed.
Building the docker image can be done by running:
pnpm docker
# or
docker build . -t flash # If `pnpm` is not available on your systemAfter the docker image is built, FLASH can be started using the following
command, where <port> is the port number you want to expose the application
on.
docker run -p <port>:3000 flashMany of FLASH's features can be configured using environment variables, which
are described in more detail below. They can be passed to the
docker container using the -e flag. For example setting the administrator
password would look like this:
docker run -p <port>:3000 -e ADMIN_PASSWORD=1234 flashAnother useful docker run flag is -v, which shares a host folder with the
docker container. It can be used in conjunction with the FS storage to persist
events and images to a local folder on the host between container runs.
docker run -p <port>:3000 -e STORAGE_BACKEND=fs -e STORAGE_DIR=/srv/flash -v <local-folder>:/srv/flash flashThe full docker run documentation can be found
here.
| Variable | Default Value | Description |
|---|---|---|
ADMIN_PASSWORD |
"Default" |
The administrator password. |
TOAST_DISPLAY_TIME |
5000 (5 seconds) |
The amount of time in milliseconds notification toasts stay on screen for. |
MULTI_FILE_UPLOAD |
false |
Whether or not to allow users to upload multiple images at once. "true" and "1" are accepted as truthy values. |
SLIDESHOW_SLIDE_DURATION |
10000 (10 seconds) |
The amount of time in milliseconds before progressing to the next slide on the slideshow. |
MAX_IMAGE_SIZE |
12582912 (12 MiB) |
The maximum image size in bytes that the user is allowed to upload. |
EVENT_REFETCH_INTERVAL |
120000 (120 seconds) |
The amount of time in milliseconds to wait before polling for changes in events. |
PHOTOS_REFETCH_INTERVAL |
12000 (12 seconds) |
The amount of time in milliseconds to wait before polling for changes in images. |
JWT_SECRET |
"SUPER_SECRET_KEY" |
The secret key to use for JWT token encryption/decryption. Keep this private. |
STORAGE_BACKEND |
"fs" |
Which storage backend to use. Currently one of "fs" or "gcloud". |
STORAGE_DIR |
$tmp/flash (N.B. The value of $tmp is OS-dependent) |
Path to the directory to store the FLASH database in. Only relevant if STORAGE_BACKEND="fs". |
GCP_BUCKET |
- | The name of the Google Cloud Storage bucket to save store the FLASH database to. Required for STORAGE_BACKEND="gcloud", ignored otherwise. |
GCP_PROJECT_ID |
- | The ID of the Google Cloud Storage project to use. Only required in conjunction with GCP_SERVICE_ACCOUNT_EMAIL and GCP_PRIVATE_KEY. |
GCP_SERVICE_ACCOUNT_EMAIL |
- | The email of the service account to use for authentication with the Google Cloud Storage project. Only required in conjunction with GCP_PROJECT_ID and GCP_PRIVATE_KEY. |
GCP_PRIVATE_KEY |
- | The private key of the service account to use for authentication with the Google Cloud Storage project. Only required in conjunction with GCP_PROJECT_ID and GCP_SERVICE_ACCOUNT_EMAIL. |
Currently, two storage backends are supported; the local file system and Google
Cloud Storage. You can switch between them by setting the STORAGE_BACKEND
environment variable.
When using Google Cloud Storage, the environment variables GCP_PROJECT_ID,
GCP_SERVICE_ACCOUNT_EMAIL and GCP_PRIVATE_KEY can all be omitted in order to
authenticate using
Application Default Credentials.
Otherwise, all three need to be provided.
- Node.js 20+
- Pnpm 10+
Project dependencies can be installed by running the following command:
pnpm installBefore the application or any tests can be run the local monorepo packages have to be built with the following command:
pnpm build:packagesIn order to run end to end tests Playwright browsers need to be installed as well. This can be done like so:
pnpm --filter website exec playwright installFLASH includes a comprehensive test suite and a mix of testing strategies across its packages to ensure reliability and correctness across all parts of the application.
| Package | Unit | Accessibility | Interaction | E2E |
|---|---|---|---|---|
Website |
✓ | ✓ | ||
file-storage |
✓ | |||
tokens |
✓ | |||
UI |
✓ | ✓ | ✓ |
To run all unit tests across all packages and the website, run
pnpm test # For all unit tests
pnpm --filter <workspace> test # To run unit test for a specific workspaceTo run End-to-End (E2E) tests for the apps/website, run
pnpm test:e2eFor storybook, the test command only runs the unit and interaction tests. For
visual and accessibility tests, start Storybook and use the UI to run the tests.
pnpm storybookAfter installing the dependencies and building the packages, you can start the main application in development mode using this command:
pnpm devThis will start a Next.js development server which compiles pages on-demand only when accessed and automatically rebuilds pages when the code changes.
The application can also be built locally and started in production mode.
pnpm build # Builds every package along with the main application
# or
pnpm --filter website build # Builds only the main application (useful if you have run `pnpm build:packages` already)
pnpm start # Starts the production server


