Skip to content

Getting Started

This walks through wiring Coldspa into a fresh project from scratch — installing both halves (CFML + Node), setting up the renderer, and rendering your first island.

  • ColdFusion (Adobe ColdFusion 2023+ or Lucee 5+)
  • Node.js 22+
  • A web server pointed at your CFML webroot
  • One of: Vue 3 / React 18+

Via CommandBox:

Terminal window
box install coldspa

This drops the package at ./modules/coldspa/, including Island.cfm, Slot.cfm, the renderer files (coldspa/renderers/Vue.cfm, coldspa/renderers/React.cfm), and the SSR sidecar.

Two things need to happen here:

  1. Register the module’s directory as a custom tag path so <cf_Island> and <cf_Slot> resolve from anywhere.
  2. Delegate the three lifecycle methods to coldspa.Bootstrap so Coldspa can cache its config and auto-spawn the Vite dev server + Node SSR sidecar.
Application.cfc
component {
this.name = "MyApp";
this.customTagPaths = expandPath("/modules/coldspa");
function onApplicationStart() {
new coldspa.Bootstrap().onApplicationStart();
return true;
}
function onApplicationStop() {
new coldspa.Bootstrap().onApplicationStop();
}
function onRequestStart(targetPage) {
new coldspa.Bootstrap().onRequestStart();
return true;
}
}

What each delegate does:

  • onApplicationStart — caches application.coldspaConfig and (in dev) spawns vite and the SSR sidecar in the background. In production it validates dist/.vite/manifest.json and runs npm run build once if it’s missing.
  • onApplicationStop — tears down the spawned Node processes so they don’t outlive the CF app.
  • onRequestStart — enables the ?reloadConfig=1 and ?reloadApp=1 dev hooks. No-op otherwise.

Logs from the spawned processes land in WEB-INF/coldspa-logs/{vite,ssr,manager}.log.

ColdBox users can skip this step entirely — the bundled ModuleConfig.cfc registers the custom tag path and hooks Bootstrap into the afterAspectsLoad, preReinit, and preProcess interceptors.

Managing Node yourself? If you’d rather run Vite/SSR under systemd, docker-compose, or your platform’s process supervisor, set COLDSPA_NO_BOOTSTRAP=1 in the CF process env. Bootstrap.onApplicationStart will still cache config but skip the spawn. Coldspa also auto-detects this when npm isn’t on PATH (the typical CF-in-Docker case) — see Docker & cross-host setups.

Coldspa uses Vite for asset bundling and a small Node sidecar for server-side rendering.

Terminal window
npm install coldspa vite vue # Vue
# or
npm install coldspa vite react react-dom @vitejs/plugin-react # React

Coldspa lists every framework package as an optional peer dependency, so you only install what you actually use.

vite.config.js
import { defineConfig } from 'vite';
import coldspa from 'coldspa/vite';
export default defineConfig({
plugins: [
coldspa({
frameworks: ['vue'], // or ['vue', 'react']
globs: {
vue: '/src/**/*.vue',
react: '/src/**/*.{jsx,tsx}'
}
})
]
});

The plugin handles framework sub-plugins, client-entry shims, manifest output, and dev-server host binding for you.

{
"scripts": {
"dev": "vite",
"ssr": "node node_modules/coldspa/coldspa/vite/ssr-server.js",
"build": "vite build && cross-env COLDSPA_SSR=1 vite build",
"ssr:prod": "cross-env NODE_ENV=production node node_modules/coldspa/coldspa/vite/ssr-server.js"
}
}

The ssr script can also be invoked via the coldspa-ssr bin shipped in the npm package.

For local single-host dev, the defaults are fine. Otherwise create a coldspa.config.json at the webroot:

{
"isDev": true,
"ssrUrl": "http://127.0.0.1:5174",
"viteUrl": "http://localhost:5173"
}

See Configuration for every key and Docker & cross-host setups when CF and Node live on different machines or containers.

src/App.vue
<script setup>
defineProps({ hello: { type: String, default: '' } });
</script>
<template>
<div class="island">
<h2>Hello, {{ hello }}!</h2>
<slot />
</div>
</template>
<!--- index.cfm --->
<cfinclude template="/modules/coldspa/coldspa/renderers/Vue.cfm">
<cf_Island
framework="#Vue#"
path="./src/App.vue"
props="#{ hello: 'World' }#"
strategy="visible">
<p>This sentence comes from the default slot in CFML.</p>
</cf_Island>

Start your CF server. That’s it — Bootstrap.onApplicationStart spawns both vite and the SSR sidecar for you in the background. Load the page and you should see your component server-rendered into the response, then hydrated when it scrolls into view.

If you’d rather drive the Node side yourself (set COLDSPA_NO_BOOTSTRAP=1 first), use two terminals:

Terminal window
npm run dev # Vite dev server (HMR)
npm run ssr # Node SSR sidecar

During iteration:

  • ?reloadConfig=1 — re-reads coldspa.config.json + env vars without bouncing the app.
  • ?reloadApp=1 — also tears down and re-spawns the Node processes.
Terminal window
npm run build # builds client bundle (dist/) AND SSR bundle (dist-ssr/)
npm run ssr:prod # runs the sidecar against the built bundle

Set isDev: false (or CF_ENV=production) so CFML reads from dist/.vite/manifest.json instead of the dev server. With Bootstrap wired up, the production sidecar (npm run ssr:prod equivalent) is launched automatically on onApplicationStart — no separate process to babysit. Set COLDSPA_NO_BOOTSTRAP=1 if you’d rather run it under systemd / docker-compose / your platform’s supervisor.