# bradtraversy/property-pulse-nextjs architecture

> Architecture diagram of the GitHub repository bradtraversy/property-pulse-nextjs, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-17). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/bradtraversy/property-pulse-nextjs
- Repository: https://github.com/bradtraversy/property-pulse-nextjs

## Overview

1. **High-level shape**
   Property Pulse is a **Next.js 14 App Router** application built around **server actions** rather than custom REST API routes. The runtime path is: browser UI → Next.js page/component layer → server actions → MongoDB, with a few external services for auth, media, maps, and geocoding.

2. **Primary user-facing areas**
   The app is organized into distinct route sections under `app/`:  
   - `app/page.jsx` home/landing experience  
   - `app/properties/*` property catalog, detail, add, edit, search results, and saved properties  
   - `app/messages/page.jsx` internal inbox  
   - `app/profile/page.jsx` user profile and listings  
   These pages are the main architectural boundaries for the user journeys.

3. **Presentation layer**
   Most UI behavior is encapsulated in `components/`, which contains reusable building blocks such as the navbar, footer, hero, property cards, forms, maps, message cards, spinners, pagination, and share/bookmark controls. This keeps the pages thin and lets route pages compose features from shared UI pieces.

4. **Server action layer**
   The `app/actions/` directory is the core application service layer. It contains targeted actions for:
   - property CRUD: add, update, delete
   - bookmarks/saved properties: bookmark, status check
   - messaging: add, delete, mark as read, unread count  
   These actions are the main boundary between UI and persistence, and they replace what would normally be API endpoints.

5. **Data model and persistence**
   MongoDB is the primary system of record, accessed through Mongoose models in `models/`:
   - `Property.js`
   - `Message.js`
   - `User.js`  
   `config/database.js` handles the DB connection setup. The architecture is document-centric: property listings, user records, and messages live in MongoDB, with relationships implied through IDs and embedded references.

6. **Authentication and authorization**
   Authentication is built with **NextAuth** and Google sign-in. The auth configuration lives in `utils/authOptions.js`, with the route handler at `app/api/auth/[...nextauth]/route.js`. `utils/getSessionUser.js` and `middleware.js` enforce user-aware behavior and route protection. In practice, this separates public browsing from protected actions like posting, editing, bookmarking, and messaging.

7. **Property lifecycle**
   The property domain is the central workflow:
   - users create/edit listings via `PropertyAddForm` and `PropertyEditForm`
   - images are uploaded and attached to a listing
   - listings appear in home/search/profile views through cards and detail pages
   - owners can edit/delete their own listings  
   The architecture assumes a listing is both a searchable catalog item and a user-owned resource.

8. **Media, maps, and location**
   Property listings are enriched with external services:
   - **Cloudinary** in `config/cloudinary.js` for image upload/storage
   - **Mapbox** and `react-map-gl` for map display
   - Google geocoding / geocode utilities for converting addresses to coordinates  
   This means the property detail page is not just data rendering; it also resolves location and media assets from external infrastructure.

9. **Messaging and notifications**
   The messaging subsystem provides internal contact between users and property owners. It includes unread counts, message read-state transitions, inbox listing, and message deletion. `UnreadMessageCount`, `MessageCard`, `PropertyContactForm`, and the related actions form a self-contained communication flow tied to a listing.

10. **Saved/bookmarked properties**
    Saved properties are handled as a separate user-centric feature, with bookmark toggling and status checks. The saved state is surfaced in its own route and UI, so the app distinguishes between catalog browsing and personalized collection management.

11. **Global client-side state and app shell**
    `context/GlobalContext.js` and `components/AuthProvider.jsx` provide app-wide client concerns, while `app/layout.jsx`, `Navbar`, `Footer`, `loading.jsx`, and `error.jsx` define the shared shell, loading state, and error boundaries. This is the outer presentation envelope that wraps all route content.

12. **Styling and design source**
    Styling is driven by **Tailwind CSS** with global styles in `assets/styles/globals.css`. The `_theme_files/` directory contains the original static HTML/Tailwind reference pages used as the design source for the Next.js implementation; it is not part of the runtime app flow, but it explains the intended structure of the UI.

13. **Static assets and environment boundaries**
    `public/images` and `assets/images` provide imagery, while `config/`, `utils/`, and `models/` form the main non-UI boundaries. The app depends on external environment configuration for MongoDB, Google auth, Cloudinary, Mapbox, and geocoding, which materially affects how the system is deployed and diagrammed.

Main entry points: [`app/layout.jsx`](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/layout.jsx) (App shell), [`components/PropertyDetails.jsx`](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/components/PropertyDetails.jsx) (Property UI), [`utils/getSessionUser.js`](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/utils/getSessionUser.js) (Session helper), [`app/properties/[id]/page.jsx`](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/[id]/page.jsx) (Property detail)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_group_app["App Router"]
  node_node_app_shell["App shell<br/>nextjs layout<br/>[layout.jsx]"]
  node_node_app_home["Home<br/>route page<br/>[page.jsx]"]
  node_node_app_properties["Properties<br/>route page<br/>[page.jsx]"]
  node_node_app_property_detail["Property detail<br/>route page<br/>[page.jsx]"]
  node_node_app_property_add["Add property<br/>route page<br/>[page.jsx]"]
  node_node_app_property_edit["Edit property<br/>route page<br/>[page.jsx]"]
  node_node_app_saved["Saved<br/>route page<br/>[page.jsx]"]
  node_node_app_search["Search results<br/>route page<br/>[page.jsx]"]
  node_node_app_messages["Messages<br/>route page<br/>[page.jsx]"]
  node_node_app_profile["Profile<br/>route page<br/>[page.jsx]"]
  node_node_actions_properties["Property actions<br/>server actions<br/>[addProperty.js]"]
  node_node_actions_bookmarks["Bookmark actions<br/>server actions"]
  node_node_actions_messages["Message actions<br/>server actions<br/>[addMessage.js]"]
end

subgraph group_group_ui["UI Components"]
  node_node_ui_shell["Shared UI<br/>react components<br/>[Navbar.jsx]"]
  node_node_ui_property["Property UI<br/>react components"]
  node_node_ui_message["Message UI<br/>react components<br/>[MessageCard.jsx]"]
end

subgraph group_group_data["Data Layer"]
  node_node_db[("MongoDB<br/>mongoose models<br/>[database.js]")]
end

subgraph group_group_infra["Infrastructure"]
  node_node_auth["Auth config<br/>[authOptions.js]"]
  node_node_session["Session helper<br/>session util<br/>[getSessionUser.js]"]
  node_node_auth_route["Auth route<br/>nextjs api route<br/>[route.js]"]
  node_node_cloudinary["Cloudinary<br/>media config<br/>[cloudinary.js]"]
  node_node_state["Global context<br/>client state<br/>[GlobalContext.js]"]
  node_node_styles["Global styles<br/>tailwind css<br/>[globals.css]"]
end

node_node_app_shell -->|"wraps"| node_node_app_home
node_node_app_shell -->|"wraps"| node_node_app_properties
node_node_app_shell -->|"wraps"| node_node_app_property_detail
node_node_app_shell -->|"wraps"| node_node_app_property_add
node_node_app_shell -->|"wraps"| node_node_app_property_edit
node_node_app_shell -->|"wraps"| node_node_app_saved
node_node_app_shell -->|"wraps"| node_node_app_search
node_node_app_shell -->|"wraps"| node_node_app_messages
node_node_app_shell -->|"wraps"| node_node_app_profile
node_node_app_home -->|"composes"| node_node_ui_shell
node_node_app_properties -->|"composes"| node_node_ui_property
node_node_app_property_detail -->|"composes"| node_node_ui_property
node_node_app_property_add -->|"composes"| node_node_ui_property
node_node_app_property_edit -->|"composes"| node_node_ui_property
node_node_app_saved -->|"composes"| node_node_ui_property
node_node_app_search -->|"composes"| node_node_ui_property
node_node_app_messages -->|"composes"| node_node_ui_message
node_node_app_profile -->|"composes"| node_node_ui_shell
node_node_app_property_add -->|"calls"| node_node_actions_properties
node_node_app_property_edit -->|"calls"| node_node_actions_properties
node_node_app_property_detail -->|"calls"| node_node_actions_bookmarks
node_node_app_saved -->|"calls"| node_node_actions_bookmarks
node_node_app_messages -->|"calls"| node_node_actions_messages
node_node_app_property_detail -->|"calls"| node_node_actions_messages
node_node_actions_properties -->|"reads/writes"| node_node_db
node_node_actions_bookmarks -->|"reads/writes"| node_node_db
node_node_actions_messages -->|"reads/writes"| node_node_db
node_node_auth_route -->|"uses"| node_node_auth
node_node_auth -->|"reads/writes"| node_node_db
node_node_session -->|"depends on"| node_node_auth
node_node_app_property_add -->|"guards"| node_node_session
node_node_app_property_edit -->|"guards"| node_node_session
node_node_app_messages -->|"guards"| node_node_session
node_node_app_profile -->|"guards"| node_node_session
node_node_ui_property -->|"uses"| node_node_cloudinary
node_node_state -->|"wraps"| node_node_auth
node_node_app_shell -->|"provides"| node_node_state
node_node_app_shell -->|"applies"| node_node_styles

click node_node_app_shell "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/layout.jsx"
click node_node_app_home "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/page.jsx"
click node_node_app_properties "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/page.jsx"
click node_node_app_property_detail "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/[id]/page.jsx"
click node_node_app_property_add "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/add/page.jsx"
click node_node_app_property_edit "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/[id]/edit/page.jsx"
click node_node_app_saved "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/saved/page.jsx"
click node_node_app_search "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/search-results/page.jsx"
click node_node_app_messages "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/messages/page.jsx"
click node_node_app_profile "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/profile/page.jsx"
click node_node_actions_properties "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/actions/addProperty.js"
click node_node_actions_bookmarks "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/actions/bookmarkProperty.js"
click node_node_actions_messages "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/actions/addMessage.js"
click node_node_ui_shell "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/components/Navbar.jsx"
click node_node_ui_property "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/components/PropertyDetails.jsx"
click node_node_ui_message "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/components/MessageCard.jsx"
click node_node_auth "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/utils/authOptions.js"
click node_node_session "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/utils/getSessionUser.js"
click node_node_auth_route "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/api/auth/[...nextauth]/route.js"
click node_node_cloudinary "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/config/cloudinary.js"
click node_node_db "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/config/database.js"
click node_node_state "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/context/GlobalContext.js"
click node_node_styles "https://github.com/bradtraversy/property-pulse-nextjs/blob/main/assets/styles/globals.css"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_node_app_shell,node_node_app_home,node_node_app_properties,node_node_app_property_detail,node_node_app_property_add,node_node_app_property_edit,node_node_app_saved,node_node_app_search,node_node_app_messages,node_node_app_profile,node_node_actions_properties,node_node_actions_bookmarks,node_node_actions_messages toneBlue
class node_node_ui_shell,node_node_ui_property,node_node_ui_message toneAmber
class node_node_db toneMint
class node_node_auth,node_node_session,node_node_auth_route,node_node_cloudinary,node_node_state,node_node_styles toneRose
```

## Components

### App Router

Route segments, pages, shell, and server actions

- [App shell](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/layout.jsx): nextjs layout · Root layout with loading, error, and not-found boundaries · `app/layout.jsx`
- [Home](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/page.jsx): route page · Landing page and featured property entry point · `app/page.jsx`
- [Properties](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/page.jsx): route page · Property catalog and browsing entry point · `app/properties/page.jsx`
- [Property detail](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/[id]/page.jsx): route page · Single property view and contact flow · `app/properties/[id]/page.jsx`
- [Add property](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/add/page.jsx): route page · Create listing flow · `app/properties/add/page.jsx`
- [Edit property](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/[id]/edit/page.jsx): route page · Update listing flow · `app/properties/[id]/edit/page.jsx`
- [Saved](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/saved/page.jsx): route page · User bookmark collection view · `app/properties/saved/page.jsx`
- [Search results](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/properties/search-results/page.jsx): route page · Filtered property listing results · `app/properties/search-results/page.jsx`
- [Messages](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/messages/page.jsx): route page · Inbox and message management view · `app/messages/page.jsx`
- [Profile](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/profile/page.jsx): route page · User profile and owned listings view · `app/profile/page.jsx`
- [Property actions](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/actions/addProperty.js): server actions · Add, update, and delete listing operations · `app/actions/addProperty.js`
- [Bookmark actions](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/actions/bookmarkProperty.js): server actions · Bookmark toggle and status checks · `app/actions/bookmarkProperty.js`
- [Message actions](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/actions/addMessage.js): server actions · Add, read, delete, and unread count operations · `app/actions/addMessage.js`

### UI Components

Reusable presentation building blocks

- [Shared UI](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/components/Navbar.jsx): react components · Navbar, footer, hero, forms, cards, maps, and widgets · `components/Navbar.jsx`
- [Property UI](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/components/PropertyDetails.jsx): react components · Property cards, details, images, map, search, and contact flow · `components/PropertyDetails.jsx`
- [Message UI](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/components/MessageCard.jsx): react components · Message cards, unread count, and submit controls · `components/MessageCard.jsx`

### Data Layer

MongoDB connection and document models

- [MongoDB](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/config/database.js): mongoose models · Database connection plus property, message, and user documents · `config/database.js`

### Infrastructure

Auth, media, state, and styling

- [Auth config](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/utils/authOptions.js): nextauth config · Google sign-in and session configuration · `utils/authOptions.js`
- [Session helper](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/utils/getSessionUser.js): session util · Session lookup and route protection helpers · `utils/getSessionUser.js`
- [Auth route](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/app/api/auth/[...nextauth]/route.js): nextjs api route · NextAuth route handler · `app/api/auth/[...nextauth]/route.js`
- [Cloudinary](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/config/cloudinary.js): media config · Image upload and storage integration · `config/cloudinary.js`
- [Global context](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/context/GlobalContext.js): client state · App-wide client context and auth provider · `context/GlobalContext.js`
- [Global styles](https://github.com/bradtraversy/property-pulse-nextjs/blob/main/assets/styles/globals.css): tailwind css · Tailwind and global CSS entrypoint · `assets/styles/globals.css`

## Connections

- App shell → Home: wraps · Shared shell frames the landing page
- App shell → Properties: wraps · Shared shell frames catalog routes
- App shell → Property detail: wraps · Shared shell frames detail routes
- App shell → Add property: wraps · Shared shell frames create flow
- App shell → Edit property: wraps · Shared shell frames edit flow
- App shell → Saved: wraps · Shared shell frames saved properties
- App shell → Search results: wraps · Shared shell frames search results
- App shell → Messages: wraps · Shared shell frames inbox
- App shell → Profile: wraps · Shared shell frames profile area
- Home → Shared UI: composes · Landing page is built from shared UI blocks
- Properties → Property UI: composes · Catalog page uses cards, pagination, and filters
- Property detail → Property UI: composes · Detail page uses images, map, sharing, and contact widgets
- Add property → Property UI: composes · Create flow uses listing form UI
- Edit property → Property UI: composes · Edit flow uses listing form UI
- Saved → Property UI: composes · Saved view uses property cards and controls
- Search results → Property UI: composes · Search results use search and listing UI
- Messages → Message UI: composes · Inbox uses message list and unread widgets
- Profile → Shared UI: composes · Profile view uses shared shell and listing widgets
- Add property → Property actions: calls · Create form submits through server actions
- Edit property → Property actions: calls · Edit form submits through server actions
- Property detail → Bookmark actions: calls · Detail view can bookmark and check saved state
- Saved → Bookmark actions: calls · Saved view depends on bookmark state
- Messages → Message actions: calls · Inbox depends on message actions and counts
- Property detail → Message actions: calls · Contact form sends listing messages
- Property actions → MongoDB: reads/writes · Property CRUD persists to MongoDB
- Bookmark actions → MongoDB: reads/writes · Bookmark state is stored with user documents
- Message actions → MongoDB: reads/writes · Messages and unread counts persist in MongoDB
- Auth route → Auth config: uses · Route handler serves the NextAuth configuration
- Auth config → MongoDB: reads/writes · Auth uses user records and session-backed account data
- Session helper → Auth config: depends on · Session helper relies on NextAuth session state
- Add property → Session helper: guards · Create listing flow requires a signed-in user
- Edit property → Session helper: guards · Edit flow requires ownership and session context
- Messages → Session helper: guards · Inbox is user-specific
- Profile → Session helper: guards · Profile content is user-specific
- Property UI → Cloudinary: uses · Property forms and images rely on media upload/storage
- Global context → Auth config: wraps · Global client state integrates auth session behavior
- App shell → Global context: provides · Root shell supplies global context to the app
- App shell → Global styles: applies · Root shell loads global styling
