A personal website and blog built with Phoenix, LiveView, and MDEx. Migrated from NextJS to Phoenix to leverage real-time features and Elixir's distributed capabilities.
Live site: aayushsahu.com
Event Horizon is a Phoenix application that serves as a personal website with:
- Static blog publishing via NimblePublisher and MDEx
- Dynamic LiveView components embedded directly in markdown
- Real-time analytics through clustered Elixir nodes
- Cross-node presence tracking for live visitor counts
- Cluster latency monitoring across distributed fly.io regions
flowchart TB
subgraph Client
Browser[Browser]
end
subgraph EventHorizon["Event Horizon (This App)"]
Endpoint[Phoenix Endpoint]
Router[Router]
LiveViews[LiveViews]
Blog[Blog Module]
Presence[Presence Tracker]
Latency[Latency Monitor]
end
subgraph BlogPipeline["Blog Pipeline"]
Markdown[Markdown Files]
Parser[NimblePublisher]
MDEx[MDEx Parser]
Plugin[MDEx Plugin]
HEEX[HEEX Components]
end
subgraph Cluster["Fly.io Cluster"]
Accumulator[Accumulator Node]
Battleship[Battleship Node]
end
Browser --> Endpoint
Endpoint --> Router
Router --> LiveViews
LiveViews --> Blog
LiveViews --> Presence
LiveViews --> Latency
Markdown --> Parser
Parser --> MDEx
MDEx --> Plugin
Plugin --> HEEX
Blog --> Parser
Presence <-.-> |PubSub| Accumulator
Latency <-.-> |ERPC| Accumulator
Latency <-.-> |ERPC| Battleship
Accumulator <-.-> |PubSub| Battleship
The blog uses a compile-time publishing system that parses markdown into optimized HTML with embedded Phoenix components.
flowchart LR
subgraph Input
MD[".md files"]
Frontmatter[YAML Frontmatter]
end
subgraph Processing
NP[NimblePublisher]
Parser[Custom Parser]
MDEx[MDEx]
Plugin[MDEx Plugin]
end
subgraph Transforms
BQ[Blockquotes β Callouts]
Links[Links β styled_anchor]
Code[Code β Syntax Highlight]
Images[Images β Lazy Load]
end
subgraph Output
Static[Static HTML]
Dynamic[Dynamic LiveComponents]
end
MD --> NP
Frontmatter --> NP
NP --> Parser
Parser --> MDEx
MDEx --> Plugin
Plugin --> BQ & Links & Code & Images
BQ & Links & Code & Images --> Static & Dynamic
Blogs can embed interactive LiveView components directly in markdown:
Here's a counter:
<.counter id="my-counter" />The dynamic: true frontmatter flag enables LiveView rendering for that blog post.
Communication between clustered nodes uses typed message structs via PubSubContract:
sequenceDiagram
participant EH as Event Horizon
participant PS as Phoenix.PubSub
participant ACC as Accumulator
Note over EH,ACC: Analytics Flow
EH->>PS: SiteVisit / BlogVisit
PS->>ACC: (distributed)
ACC->>PS: SiteUpdated / BlogUpdated
PS->>EH: (distributed)
Note over EH,ACC: Presence Flow
EH->>PS: SitePresence / BlogPresence
PS->>ACC: (distributed)
ACC->>PS: PresenceRequest
PS->>EH: (distributed)
The Latency GenServer continuously measures RTT between nodes using :erpc:
flowchart LR
EH[Event Horizon<br/>Frankfurt]
PHX[Accumulator<br/>Frankfurt]
BSH[Battleship<br/>Frankfurt]
EH <-->|erpc.call| PHX
EH <-->|erpc.call| BSH
PHX <-->|erpc.call| BSH
View live latencies at /cluster.
lib/
βββ event_horizon/
β βββ blog/
β β βββ article.ex # Article struct
β β βββ converter.ex # HTML conversion
β β βββ mdex_plugin.ex # AST transformations
β β βββ parser.ex # Frontmatter parsing
β βββ blog.ex # NimblePublisher config
β βββ presence.ex # Phoenix.Presence for visitors
β βββ latency.ex # Cross-node latency GenServer
β βββ pubsub_contract.ex # Typed PubSub messages
β βββ rss.ex # RSS feed generation
β βββ sitemap.ex # Sitemap generation
βββ event_horizon_web/
β βββ components/ # HEEX components
β βββ live/ # LiveView modules
β β βββ blog_live/ # Blog pages
β β βββ cluster_live.ex # Cluster status page
β β βββ ...
β βββ router.ex
βββ mix/ # Custom mix tasks
priv/
βββ posts/ # Markdown blog posts
- Elixir 1.18+
- Erlang/OTP 27+
# Install dependencies and setup
mix setup
# Start the server
mix phx.server
# Or with IEx
iex -S mix phx.serverVisit localhost:4000.
| Task | Description |
|---|---|
mix setup |
Install deps, build assets, generate RSS and sitemap |
mix rss.generate |
Generate RSS feed |
mix sitemap.generate |
Generate sitemap.xml |
mix precommit |
Run compile checks, format, and tests |
Deployed on fly.io with DNS clustering enabled. The app automatically connects to other nodes in the same organization.
fly deployKey libraries:
- Phoenix - Web framework
- Phoenix LiveView - Real-time UI
- MDEx - Markdown parsing with HEEX support
- NimblePublisher - Static content publishing
- Image - Blog banner generation
- Autumn - Syntax highlighting (via MDEx)
MIT