import { html, appendTo, state } from 'pulsjs'
const name = state('John')
appendTo(document.body, html`
<h1>Hello ${name}!</h1>
<input :bind=${name}>
`)
Puls is a reactive web framework that uses the DOM directly. It is inspired by Svelte and Vue. Puls is designed to be simple and fast. It is a great choice for small to medium-sized projects.
No compiler is required but if you want, there are .puls component files or JSX support.
npm install pulsjs
Create a typescript or javascript project with PulsJS and Vite
npm create pulsjs@latest my-app-name
<script type="module">
import { html, appendTo, state } from 'https://cdn.skypack.dev/pulsjs'
...
</script>
- Puls uses the DOM directly (no virtual DOM)
- Reactive state
- Computed values
- Components
- Control flow
- Event handling
- Bindings
import { state, computed, watch, html, appendTo } from 'pulsjs'
const name = state('John')
const computedValue = computed(() => `I'm happy that you are named ${name.value}`)
watch([name], () => {
console.log('Name changed')
})
appendTo(document.body, html`
<h1>Hello ${name}!</h1>
${computedValue}
<input :bind=${name}>
`)
import { track, state } from 'pulsjs'
const hello = state({
hello: 'World',
world: 'example'
}, { deep: true })
// only refreshes when hello changes
watch([track(() => hello.value.hello)], () => {
console.log('Hello changed')
})
import { html } from 'pulsjs'
function ExampleComponent({ example }) {
return html`
<p>Example component ${example}</p>
`
}
html`
<${ExampleComponent} example="hello world" />
`
import {defineEmits, defineProps, defineSlot, html, onMounted} from "pulsjs";
function ExampleComponent() {
const props = defineProps<{
example: string;
}>()
const emit = defineEmits()
const slot = defineSlot<Node[]>()
onMounted(() => {
console.log('Mounted')
})
onUnmounted(() => {
console.log('Unmounted')
})
return html``
}
import { html } from 'pulsjs'
class ExampleComponent extends PulsComponent {
example = state('val')
setup() {
console.log('Setup')
}
render() {
return html`
<p>Example component ${this.example}</p>
`
}
}
registerComponent('example-component', ExampleComponent)
// Typescript
@CustomElement('example-component')
export class ExampleComponent extends PulsComponent {}
const a = state('test')
html`
<${ExampleComponent} example=${a} />
`
export class ExampleComponent extends PulsComponent {
test = state('hello')
exampleObject = state({})
// ...
}
registerComponent('example-component', ExampleComponent)
document.body.innerHTML = `
<example-component test="hello"></example-component>
<example-component :exampleObject="{\"key\": 4}"></example-component>
`
import { html, appendTo, state, computed } from 'pulsjs'
const counter = state(1)
html`
<!-- js ternary operator -->
${computed(() =>
counter.value === 1 ? html`<div>Value is 1</div>` :
counter.value === 2 ? html`<div>Value is 2</div>` :
html`Value is something else`
)}
<!-- attributes -->
<div :if=${() => counter.value === 1}>
Value is 1
</div>
<div :else-if=${() => counter.value === 1}>
Value is 2
</div>
<div :else>
Value is something else
</div>
<button @click=${() => counter.value++}>Increment ${counter}</button>
`
Example ExampleComponent.puls
<script>
import { state } from 'pulsjs'
const name = state('John Doe');
</script>
<h1>${name}</h1>
<input :bind=${name} />
npm install pulsjs-scss
import { PulsComponent, CustomElement, html } from 'pulsjs'
import { scss } from 'pulsjs-scss'
export class ExampleComponent extends PulsComponent {
render() {
return html`
example
`
}
styles() {
return scss`
example {
color: red;
}
`
}
}
export function Test() {
const name = state('John')
return (
<div>
{name}
<input p-bind={name} />
<button on:click={() => name.value = 'test'}>Click me</button>
</div>
)
}
npm install pulsjs-router
import { PulsComponent, CustomElement, html } from 'pulsjs'
import { Router } from 'pulsjs-router'
const router = new Router([
{
path: '/',
name: 'home',
view: () => html`
<div>
<h1>Router Works!</h1>
</div>
`
},
{
path: '/test',
name: 'test',
view: () => html`
<div>
<h1>Router page 2!</h1>
</div>
`
},
{
path: '/*',
name: '404',
view: () => html`
<div>
<h1>404</h1>
</div>
`
}
// Also supports nested routes with children and layouting
])
appendTo(document.body, html`
<div>
${router.link}
</div>
<${router.link} to=${{name: 'home'}}>Home</${router.link}>
<${router.link} to="/test">Test Page</${router.link}>
`)
router.init()
Use pnpm
pnpm run build
pnpm test