This repository contains the source code for the official website as well as the official app of conveniat27, built with Next.js and Payload CMS.
- Core Technologies
- Prerequisites
- Getting Started
- Project Structure
- Key Concepts
- Code Quality & Conventions
- UI Component Library
- Environment Variables
- License
- Framework: Next.js (App Router)
- CMS: Payload CMS (Headless, Self-hosted)
- Language: TypeScript (with strict type checking)
- UI: React, shadcn/ui, Tailwind CSS, Headless UI
- Icons: Lucide React
- Database: MongoDB (self-hosted), MinIO (S3-compatible object storage, self-hosted), PostgreSQL (self-hosted)
- PWA: Serwist (for Service Worker management)
- Code Quality: ESLint, Prettier
- Development Environment: Docker (Devcontainer)
Ensure you have the following installed on your system:
- Git
- Docker & Docker Compose
- An IDE that supports Devcontainers (e.g., VS Code with the Dev Containers extension, WebStorm).
- Clone the repository
- Copy the
.env.example
file to.env
and fill empty values. - Open the project using the provided devconatiner inside your IDE (VSCode or Webstorm are tested).
- Start Developing using the following commands:
The above command launches a local development server with hot-reloading enabled. You can open the website on
docker compose up --build --watch
http://localhost:3000
.
- Install Dependencies:
pnpm install
- Start Development Server:
docker compose up --build --watch
- Stop Development Server:
docker compose down
- Clear Database & Volumes: To completely reset the database and remove Docker volumes (useful for reseeding):
After running this, you'll need to restart the server with
docker compose down --volumes
docker compose up --build --watch
to re-initialize and potentially re-seed the database based on Payload's configuration.
Once the development server is running, you can typically access the Payload CMS admin interface at:
http://localhost:3000/admin
(or your configured admin route)
The project structure is influenced by Next.js App Router conventions and principles from Bulletproof React, emphasizing modularity and maintainability.
public/ # Static assets (images, fonts, sw.js, etc.)
src/
|
+-- app/ # Next.js App Router: Layouts, Pages, Route Handlers
| |-- (entrypoint)/ # Entrypoint for the APP (manually localized)
| |-- (payload)/ # Routes related to Payload Admin UI
| |-- (frontend)/ # Routes for the main website frontend / app
|
+-- components/ # Globally shared React components
|
+-- config/ # Global application configurations (e.g., exported env vars)
|
+-- features/ # Feature-based modules (self-contained units of functionality)
| |-- service-worker/ # Serwist service worker logic
| |-- payload-cms/ # Payload CMS specific configurations, collections, globals, hooks
| +-- ... # Other features
|
+-- hooks/ # Globally shared React hooks
|
+-- lib/ # Globally shared utility functions, libraries, clients
|
+-- types/ # Globally shared TypeScript types and interfaces
|
+-- utils/ # Globally shared low-level utility functions
- Most application logic resides within the
src/features
directory. - Each sub-directory in
src/features
represents a distinct feature (e.g.,chat
,map
,payload-cms
). - Encapsulation: Code within a feature folder should primarily relate to that specific feature.
- Structure within Features: A feature can internally have its own
components
,hooks
,api
,types
,utils
subdirectories, scoped to that feature. - Import Restrictions: ESLint rules (
import/no-restricted-paths
ineslint.config.mjs
) enforce unidirectional dependencies:app
can import fromfeatures
and shared directories (components
,hooks
, etc.).features
cannot import fromapp
or shared directories.- Features generally should not import directly from other features, promoting loose coupling. Exceptions are
explicitly defined (e.g.,
payload-cms
andnext-auth
can be imported more broadly). - Shared directories (
components
,hooks
,lib
,types
,utils
) should not import fromapp
orfeatures
.
- Payload CMS Exception: The
payload-cms
feature is central and can be imported by other parts of the application as it defines the core data structures / content types used throughout the app.
This structure aids scalability, maintainability, and team collaboration by keeping concerns separated.
A core aspect of this project is that most frontend pages are dynamically generated based on data managed within Payload CMS.
- CMS Configuration (
src/features/payload-cms/payload.config.ts
,src/features/payload-cms/settings
): Defines data structures (Collections, Globals) and their fields. Collections might represent page types, blog posts, etc. - Routing (
src/app/(frontend)/[locale]/(payload-pages)/[...slugs]/page.tsx
): This dynamic route catches most frontend URL paths. - Route Resolution: The application resolves the incoming URL (
slugs
) against Collections and Globals defined in Payload CMS (via thesrc/features/payload-cms/routeResolutionTable.ts
). - Layout & Component Mapping: Once the corresponding CMS data is found for a URL, a specific page layout (
src/features/payload-cms/page-layouts
) is rendered. Complex CMS fields (like Blocks or Rich Text) are mapped to React components using converters (src/features/payload-cms/converters
).
This application utilizes Serwist (@serwist/next
) to implement Service Worker
functionality, enabling PWA features:
- Offline Access: Pre-cached pages (like the
/offline
page) and potentially other assets allow basic functionality when the user is offline. - Caching: Improves performance by caching assets and network requests.
- Reliability: Provides a more resilient user experience on flaky networks.
The service worker logic is defined in src/features/service-worker/index.ts
and configured in next.config.mjs
. It's
generally disabled in development unless ENABLE_SERVICE_WORKER_LOCALLY=true
is set.
Maintaining code quality and consistency is crucial.
The project enforces strict TypeScript settings (tsconfig.json
), including:
strict
, strictNullChecks
, noImplicitAny
, noUncheckedIndexedAccess
, exactOptionalPropertyTypes
, etc. This helps
catch errors at compile time and improves code reliability.
- ESLint (
eslint.config.mjs
): Used for identifying and reporting on patterns in JavaScript/TypeScript code. Includes rules fromeslint:recommended
,typescript-eslint
,unicorn
,react-hooks
,next/core-web-vitals
, and custom rules for conventions and import restrictions. - Prettier: Used for automatic code formatting to ensure a consistent style. Integrated via
eslint-plugin-prettier
. - Run Checks: (Ensure these scripts exist in your
package.json
)# Run ESLint checks and fix issues pnpm run lint
As mentioned in the Project Structure section, ESLint rules strictly enforce module boundaries to
maintain a clean and understandable architecture. Path aliases (@/*
, @payload-config
) defined in tsconfig.json
are
used for cleaner imports.
- shadcn/ui: Provides beautifully designed, accessible components built on Radix UI and
Tailwind CSS. Components are typically copied into the project (
src/components/ui
) rather than installed as a dependency. - Headless UI: Used for unstyled, accessible UI components providing underlying logic for elements like modals, dropdowns, etc.
- Lucide React: Provides a wide range of clean and consistent SVG icons.
- Configuration is managed via environment variables.
.env.example
serves as a template listing the required variables.- Create a
.env
file (copied from.env.example
) for local development. Never commit.env
files to Git. - Populate
.env
with necessary credentials (database URLs, API keys, secrets, etc.).
The easiest way to build the page into a production ready bundle is to use the provided Docker Compose file. This will build the Next.js application and Payload CMS, and prepare it for deployment.
docker compose -f docker-compose.prod.yml up --build
However, you can also build the application manually using the following commands.
Please ensure that you have deleted node_modules
, src/lib/prisma/*
, and .next
before running the commands to ensure a clean build.
Also make sure that you DON'T have any .env
file in the root of the project, as this will
cause issues with the build process.
# Export environment variables
export $(grep -v '^#' .env | grep '^NEXT_PUBLIC_' | xargs)
export BUILD_TARGET="production"
export NODE_ENV="production"
export DISABLE_SERVICE_WORKER="true" # speeds up build process (optional)
export PRISMA_OUTPUT="src/lib/prisma/client/"
# Install dependencies
pnpm install
# Create build info file
bash create_build_info.sh
# Generate Prisma client
npx prisma generate
# Build the Next.js application
pnpm next build
To analyze the bundle size of the Next.js application, you can use the next-bundle-analyzer
package.
Xou can run the following command to analyze the bundle size. This will generate a report and open it in
your default browser.
ANALYZE=true pnpm build
This project is licensed under the MIT License — see the LICENSE
file for details.