Prism
(No longer in development). Experimental compiler for building isomorphic web applications with web components.
Install / Use
/learn @kaleidawave/PrismREADME
<img src="static/logo.png" height="24" /> Prism Compiler
Prism is a experimental compiler that takes declarative component definitions and creates lightweight web apps. Prism is not a stable production framework, instead a proof of concept of a better isomorphic implementations. Prism is built from the ground up. All HTML, CSS and JS parsing and rendering is done under a internal library known as chef.
Install with:
> npm install -g @kaleidawave/prism
> prism info
(not to be confused with highlighting library prismjs and database toolkit prisma)
Quick start tutorial
Ultra efficient isomorphic. No JSON state, No rerender on hydration:
Prism compiles in getter functions for getting the state from the HTML markup. Events listeners are added with no need to rerender. The generated client side code is designed to work with existing HTML or elements generated at runtime. Virtualized state means that state can exist without being in the JS vm. When state is needed only then is it loaded into JS and cached for subsequent gets. This avoids the large JSON state blobs that exist on all other isomorphic solutions. This solution works for dynamic HTML. This should lead to smaller payloads and a faster time to interactive.
Server side rendering on non JS runtime:
For the server, Prism compiles components to ultra fast string concatenations avoiding the need for server side DOM. Prism can also compile string concatenation functions for Rust lang. See the Prism Hackernews Clone. This allows to write the markup once avoiding desync hydration issues and the time spent rewriting the render functions. It also acts as a checking step verifying correct HTML and type issues. Hopefully more backend languages in the future
Super small runtime:
Prism counter example compiles to 2kb (1kb gzip). According to webcomponents.dev this makes Prism the smallest framework. Of that bundle size 1.41kb is prism runtime library.
There is also the benefit that Prism does not need as JSON blob to do hydration on the client side. So for other frameworks, even if your bundle.js is 10kb you may have another 6kb of preload data sent down with each request as well that needs to be parsed, loaded etc. With Prism the only JS that is needed is the bundle.
Web components authorization:
Prism compiles down to native web components. Prism takes HTML templates and compiles them into native DOM api calls. It takes event bindings and compiles in attaching event listeners. Prism can output single component definitions that can be shared and work natively. Building a app with Prism consists of batch component compilation and injecting a client side router to build a SPA.
Development:
Prism does not have any editor plugins. However association .prism files to be interpreted as HTML works well as Prism is a extension of HTML. Although it does not provide full intellisense you get all the syntax highlighting and emmet.
"files.associations": {
"*.prism": "html"
}
Single file components and templating syntax:
Prism uses a similar style single file components to vue and svelte:
<template>
<h3>Counter</h3>
<h5 $title="count">Current count: {count}</h5>
<button @click="increment">Increment</button>
</template>
<script>
@Default({count: 0})
class CounterComponent extends Component<{count: number}> {
increment() {
this.data.count++;
}
}
</script>
<style>
h5 {
color: red;
}
</style>
Text interpolation is handled by inclosing any value inside {}. To make a attribute dynamic it is prefixed with $. For events the key is the name of the event prefixed with @ and the value points to the name of a method or function. In the following examples you will see a type argument sent to component which corresponds to the data type. This helps Prism with returning state from markup as the markup is all text and there may need to be numbers etc. It is also used by the reactivity binding framework for creating deep observables.
For importing components:
<template>
<h3>{postTitle}</h3>
...
</template>
<script>
// It is important that the class is exported
export class PostComponent extends Component<{postTitle: string}> {}
</script>
<template>
<PostComponent $data="post"></PostComponent>
</template>
<script>
import {PostComponent} from "./postComponent.prism";
...
</script>
For slots / sending children to components the <slot></slot> component is used:
<template>
<div class="some-wrapper">
<!-- It is important that the slot is a single child -->
<slot></slot>
</div>
</template>
Conditionals rendering:
<template>
<!-- If with no else -->
<div #if="count > 5">Count greater than 5</div>
<!-- If with else -->
<div #if="count === 8">Count equal to 8</div>
<div #else>Count not equal to 8</div>
</template>
Iterating over arrays:
<template>
<ul #for="const x of myArray">
<li>{x}</li>
</ul>
</template>
For dynamic styles:
<template>
<h1 $style="color: userColor;">Hello World</h1>
</template>
<script>
interface IComponentXData {color: string}
class ComponentX extends Component<IComponentXData> {
setColor(color) {
this.data.userColor = color;
}
}
</script>
Client side routing
<template>
<h1>User {username}</h1>
</template>
<script>
@Page("/user/:username")
class ComponentX extends Component<{username: string}> {
// "username" matches the value of the parameter username specified in the url matcher:
load({username}) {
this.data.username = username;
}
}
</script>
Performing a client side routing call can be done directly on a anchor tag:
<template>
<!-- "relative" denotes to perform client side routing -->
<!-- href binding is done at runtime so href can be dynamic attribute -->
<a relative $href="`/user/${username}`">
</template>
or in javascript
await Router.goTo("/some/page");
There is also layouts which when the page is routed to will be inside of the layout. Layouts use the previous slot mechanics for position the page.
...
<script>
@Layout
export class MainLayout extends Component {}
</script>
...
<script>
import {MainLayout} from "./main-layout.prism"
@Page("/")
@UseLayout(MainLayout)
export class MainLayout extends Component {}
</script>
(Also note Layouts extends Components and can have a internal state)
Web components
Prism components extend the HTMLElement class. This allows for several benefits provided by inbuilt browser functionality:
- Firing native events on component
- Reduced bundle size by relying on the browser apis for binding JS to elements
- Standard interface for data (with the hope of interop with other frameworks)
Web component compilation:
One of the problems of web component is that to issue a single component with a framework like React, Vue or Angular you also have to package the framework runtime with the component. This means if you implement a web component built with vue and another built with React into you plain js site you have a huge bundle size with two frameworks bundled. Web component are meant to be modular and lightweight which is not the case when 90% of the component is just framework runtime.
Prism attempts to move more information to build time so that the runtime is minimal. As it leaves reactivity to runtime it allows data changes to be reflected in the view. It also provides the ability to detect mutation so array methods like push and pop can be used.
Rust backend compilation:
As of 1.3.0 prism supports compiling server render functions to native rust functions. These functions are framework independent, fast string concatenations and strongly typed. Obviously transpiling between is incredibly difficult and while Prism can create its own Rust ast it can't really convert custom use code from TS to Rust. So there is a decorator that can be added to functions @useRustStatement that will insert the value into the Rust module rather than the existing function definition. This code can do an import or as shown in the example below redefine the function:
<template>
<h1>{uppercase(x)}</h1>
</template>
<script>
@useRustStatement(`fn uppercase(string: String) -> String { return string.to_uppercase(); }`)
function uppercase(str: string) {
return str.toUpperCase();
}
@Globals(uppercase)
class SomeComponent extends Component<{x: string}> {}
</script>
Other decorators and methods:
<script>
@Ta
Related Skills
node-connect
353.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Writing Hookify Rules
111.7kThis skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
review-duplication
100.7kUse this skill during code reviews to proactively investigate the codebase for duplicated functionality, reinvented wheels, or failure to reuse existing project best practices and shared utilities.
