Static / Server-Rendered Pages
ShipClojure supports two approaches to server-rendered pages:
1. Content pages (Blog, ToS, Privacy)
These are managed by the Powerpack content system and rendered as static HTML using UIX components. See Blog & Content System for full documentation.
Content pages are written in markdown, stored in content/, and rendered via:
content/*.md → Datomic → UIX components → HTML string → static files
In development, pages render dynamically from the in-memory DB. In production, they're pre-exported as static HTML.
2. Interactive landing pages (UIX SSR + hydration)
For pages that need both server-rendered HTML (for SEO/performance) and client-side interactivity, use UIX's server-side rendering with client-side hydration.
The key insight: write your components as .cljc files so they can render on both the JVM (static HTML) and in the browser (interactivity).
How it works
- Server: UIX renders your component tree to an HTML string via
uix.dom.server/render-to-static-markup - Client: Shadow-CLJS loads a small JS bundle that re-renders the same component tree, adding event handlers
Shadow-CLJS module splitting
Split your JS into modules so static pages don't load the full SPA bundle:
;; shadow-cljs.edn
{:builds
{:app {:target :browser
:modules {:base {:entries [saas.base]}
:landing {:init-fn saas.landing/init!
:depends-on #{:base}}
:main {:init-fn saas.core/init!
:depends-on #{:base}}}}}}
- Static pages load
base.js+landing.js(small) - SPA loads
base.js+main.js(full app)
Layout integration
The content layout system (src/cljc/saas/content/ui/layout.cljc) handles script injection. The base-layout includes content.js for theme switching and TOC highlighting. The content-layout adds Prism.js for code syntax highlighting.
For interactive pages that need hydration, create a custom layout that includes the appropriate JS module.
Read this blog post for a deeper dive into UIX SSR with shadow-cljs.