celestite
Version, currently 0.1.37 versions
github.com/noahlh/celestite
Beautifully reactive, server-side rendered Svelte apps w/ a Crystal backend
239 stars
0 dependents
License: MIT
Nothing has been indexed for 0.1.3 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.
Installation
# Add this to your shard.yml
dependencies:
celestite:
github: noahlh/celestite
version: ~> 0.1.3Then run:
shards installshard.yml
No shard.yml has been indexed for 0.1.3. You can read it on the repository.
Dependencies
Unknown: the shard.yml for this version has not been read yet.
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.

# celestite
<img src="https://crystal-lang.org/assets/media/crystal_icon.svg?sanitize=1" height=21> Crystal + <img src="https://upload.wikimedia.org/wikipedia/commons/1/1b/Svelte_Logo.svg" height=16> Svelte = :zap:
Celestite allows you to use the full power of [Svelte](https://svelte.dev) reactive components in your [Crystal](https://crystal-lang.org) 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.](https://nlh.me/projects/celestite)
### 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](http://kemalcr.com/), but there's no reason it won't work with [Amber](https://amberframework.org), [Lucky](https://luckyframework.org/), [Athena](https://athenaframework.org), 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`
```yaml
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:
```crystal
require "celestite"
include Celestite::Adapter::Kemal
```
### 3. Add initialization code
Create an initializer file (e.g., `/config/initializers/celestite.cr`):
```crystal
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](/config/celestite_init.example.cr) for more options.
### 4. Add a static route for your build_dir
For Kemal:
```crystal
# 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
```crystal
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 `.svelte` extension)
- `context` - A `Celestite::Context` hash with data to pass to your component
- `layout` - Optional HTML layout file from your layout_dir
### Example
```html
<!-- 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>
```
```crystal
# 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
```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.
```svelte
<script>
import { onMount } from 'svelte';
onMount(() => {
// Browser-only code here
console.log(window.location);
});
</script>
```
or
```svelte
<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
1. Install mkcert:
```bash
brew install mkcert # macOS
```
2. Install the local CA:
```bash
sudo mkcert -install
```
3. Generate certificates:
```bash
mkcert -key-file dev.key -cert-file dev.crt localhost 127.0.0.1 ::1
```
4. Enable in configuration:
```crystal
Celestite.initialize(
dev_secure: true,
# ... other config
)
```
Celestite reads `dev.key` and `dev.crt` from `ROOT_DIR` when `dev_secure` is enabled.
For sandboxed or proxied development, "set" means exporting the variables in the
parent app process environment before Celestite initializes.
Use:
```bash
DEV_CLIENT_BASE=/_vite
DEV_CLIENT_PROTOCOL=https
```
For example:
```bash
ENV=development DEV_CLIENT_BASE=/_vite DEV_CLIENT_PROTOCOL=https crystal run src/trader.cr
```
or, if your shell already has those variables exported:
```bash
./cmon trader
```
`DEV_CLIENT_BASE=/_vite` tells Celestite to load Vite modules from the same public
origin under `/_vite/...` instead of from a separate `:5173` origin. In authenticated
previews, that keeps module loads and HMR on the authenticated origin.
`DEV_CLIENT_PROTOCOL=https` is the correct companion setting when the public app URL
is HTTPS.
## Production Builds
For production, Svelte components must be pre-built using Vite.
### Building
From your app's root directory:
```bash
# 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:
```bash
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 hashes
- `BUILD_DIR/client/.vite/manifest.json` - Asset manifest for hydration
- `BUILD_DIR/server/` - SSR modules for server-side rendering
### Testing Production Builds Locally
```bash
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 |
| `dev_client_protocol` | - | Override the browser-facing dev protocol |
| `dev_client_base` | - | Prefix browser-facing dev assets |
| `disable_a11y_warnings` | `false` | Suppress Svelte accessibility warnings |
## Roadmap
- [x] Svelte 5 support with Vite
- [x] Hot Module Reloading (HMR)
- [x] 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.
1. Fork it (<https://github.com/noahlh/celestite/fork>)
2. Create your feature branch (`git checkout -b my-feature`)
3. Write tests!
4. Commit your changes (`git commit -am 'Add feature'`)
5. Push to the branch (`git push origin my-feature`)
6. Create a Pull Request
## Contributors
- Noah Lehmann-Haupt (nlh@nlh.me / [noahlh](https://github.com/noahlh)) - creator, maintainer
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.1.3- Tagged
- Mar 20, 2026
- Commit
ed08825c0981- Indexed
- not yet
Dependents
No indexed shard depends on this one yet.
Repository
github.com/noahlh/celestite
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 13, 2026
- Synced
- Aug 13, 2026
- Versions
- 7