celestite v0.2.1
celestite
Crystal +
Svelte = ⚡️
Celestite allows you to use the full power of Svelte reactive components in your Crystal web apps. It's a drop-in replacement for your view layer -- no more need for intermediate .ecr templates. With celestite, you write your backend server code in Crystal, your frontend client code in JavaScript & HTML, and everything works together seamlessly...and fast.
Introduction
Read the full introductory blog post here.
Requirements
- Crystal 1.0.0+
- Bun 1.0+ (for the SSR render server)
Installation
THIS IS PREVIEW / EARLY ALPHA SOFTWARE
This is not much more than a proof-of-concept at the moment, but it does work! Standard warnings apply - it will likely break/crash in spectacular and ill-timed glory, so don't poke it, feed it past midnight, or use it for anything mission-critical (yet).
Celestite has been developed / tested with Kemal, but there's no reason it won't work with Amber, Lucky, Athena, etc. (but no work integrating with those has been done yet.) The steps below assume you'll be working with Kemal.
1. Add celestite to your application's shard.yml and run shards install
dependencies:
celestite:
github: noahlh/celestite
version: ~> 0.2.0
The postinstall hook will automatically install JavaScript dependencies via Bun.
2. Include the helper:
For Kemal:
require "celestite"
include Celestite::Adapter::Kemal
3. Add initialization code
Create an initializer file (e.g., /config/initializers/celestite.cr):
require "celestite"
Celestite.initialize(
engine: Celestite::Engine::Svelte,
component_dir: "#{Dir.current}/src/views/",
build_dir: "#{Dir.current}/public/celestite/",
port: 4000,
vite_port: 5173,
)
See example config for more options.
4. Add a static route for your build_dir
For Kemal:
# myapp.cr
add_handler Kemal::StaticFileHandler.new("./public/celestite")
5. Add your .svelte files and start building!
Name your root component index.svelte (all lowercase).
Usage
celestite_render
celestite_render(component : String?, context : Celestite::Context?, layout : String?)
Call this where you'd normally call render in your controllers.
component- The Svelte component to render (without.svelteextension)context- ACelestite::Contexthash with data to pass to your componentlayout- Optional HTML layout file from your layout_dir
Example
<!-- src/views/layouts/layout.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- CELESTITE HEAD -->
<!-- The above comment is actually needed - Celestite looks for it and injects optional svelte:head content -->
</head>
<body>
<div id="celestite-app">
<!-- CELESTITE BODY -->
<!-- The above comment is also actually needed - Celestite looks for it and injects the server-side rendered component -->
</div>
<!-- CELESTITE CLIENT -->
<!-- The above comment is also also actually needed - Celestite looks for it and injects the client-side bundle -->
</body>
</html>
# myapp.cr
get "/test" do
context = Celestite::Context{ data: "Hello from Crystal!" }
celestite_render(component:"Home.svelte", context: context, layout: "layout.html")
end
Accessing Context in Svelte
<script>
let { context } = $props();
</script>
<h1>Result: {context.data}</h1>
Server vs Client Rendering
Your .svelte components are automatically rendered server-side before being sent to the client, then hydrated on the client for interactivity.
Code that relies on browser-specific APIs (like document or window) must be wrapped in Svelte's onMount() or otherwise guarded.
<script>
import { onMount } from 'svelte';
onMount(() => {
// Browser-only code here
console.log(window.location);
});
</script>
or
<script>
let isBrowser = false;
if (typeof window !== 'undefined') {
isBrowser = true;
}
</script>
{#if isBrowser}
<!-- Browser-specific content -->
{/if}
HTTPS/SSL Support for Development
Celestite supports running the Vite dev server over HTTPS, useful for tunneled connections (ngrok, localtunnel, etc.).
Setup
-
Install mkcert:
brew install mkcert # macOS -
Install the local CA:
sudo mkcert -install -
Generate certificates:
mkcert -key-file dev.key -cert-file dev.crt localhost 127.0.0.1 ::1 -
Enable in configuration:
Celestite.initialize( dev_secure: true, # ... other config )
Production Builds
For production, Svelte components must be pre-built using Vite.
Building
From your app's root directory:
# Build client bundles
COMPONENT_DIR=/path/to/views BUILD_DIR=/path/to/public/celestite \
bunx --bun vite build --config /path/to/lib/celestite/vite.config.js
# Build SSR bundles
COMPONENT_DIR=/path/to/views BUILD_DIR=/path/to/public/celestite \
bunx --bun vite build --config /path/to/lib/celestite/vite.config.js --ssr
Or use the Makefile target:
cd /path/to/lib/celestite/src/svelte-scripts
make build COMPONENT_DIR=/path/to/views BUILD_DIR=/path/to/public/celestite
Build Output
BUILD_DIR/client/- Client-side JS/CSS with content hashesBUILD_DIR/client/.vite/manifest.json- Asset manifest for hydrationBUILD_DIR/server/- SSR modules for server-side rendering
Testing Production Builds Locally
NODE_ENV=production NODE_PORT=4000 \
COMPONENT_DIR=/path/to/views \
LAYOUT_DIR=/path/to/views/layouts \
BUILD_DIR=/path/to/public/celestite \
bun run /path/to/lib/celestite/src/svelte-scripts/vite-render-server.js
Configuration Options
| Option | Default | Description |
|---|---|---|
engine |
Svelte |
Rendering engine (currently only Svelte) |
component_dir |
- | Path to your Svelte components |
layout_dir |
- | Path to HTML layout templates |
build_dir |
- | Output directory for production builds |
port |
4000 |
Bun SSR server port |
vite_port |
5173 |
Vite dev server port (development only) |
dev_secure |
false |
Enable HTTPS for dev server |
disable_a11y_warnings |
false |
Suppress Svelte accessibility warnings |
Roadmap
- Svelte 5 support with Vite
- Hot Module Reloading (HMR)
- Production builds with content hashing
- Example/demo project
Contributing
Contributions are welcome! This is an open source project and feedback, bug reports, and PRs are appreciated.
- Fork it (https://github.com/noahlh/celestite/fork)
- Create your feature branch (
git checkout -b my-feature) - Write tests!
- Commit your changes (
git commit -am 'Add feature') - Push to the branch (
git push origin my-feature) - Create a Pull Request
Contributors
- Noah Lehmann-Haupt (nlh@nlh.me / noahlh) - creator, maintainer
celestite
- 235
- 9
- 26
- 0
- 0
- 2 days ago
- July 18, 2018
MIT License
Fri, 12 Dec 2025 23:50:07 GMT