Skip to content
/ prelude Public

A signal-based web library that lets you develop in a familiar functional style

License

Notifications You must be signed in to change notification settings

wrnrlr/prelude

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Prelude

Home NPM JSR

Prelude lets you develop web applications in a familiar component-based functional style. It is build with the desire to have a lightweight frontend framework that works using just JavaScript but that nontheless can handle complex web applications without sacrificing on developer expierence.

Get Started

Prelude works with most popular JavaScript runtimes: Node, Deno, Bun or the borwser. It is available on NPM and JSR under the package named @wrnrlr/prelude.

The quickest way to get started with Prelude is uing the Playground app on the homepage. It offers a IDE complete with a code editor, live preview and a number of examples. Aternativaly you can develop on your local machine using vite.

Some Prelude APIs can also be used in the REPL to expore their behaviour interactively.

Basic Example

This is a example of a button that increments a counter when it is clicked.

<!DOCTYPE html>
<title>Counter</title>

<script type="module">
  import {h, signal, render} from 'https://esm.sh/@wrnrlr/prelude'

  function Counter() {
    const n = signal(1)
    return h('button', {onClick: e => n(n=>n+1)}, n)
  }

  render(Counter, document.body)
</script>

Hyperscript

Prelude does not use JSX or a templating language to descript html instead we use a DSL called HyperScript.

The h function is used in either of two ways based on the type of the first argument, when it is a string it will create a html element like , and when it is a function it will create a reactive component.

h('div',{},[
  h('label','Name'),h('input',{})
])

Event Handler

Prelude tries to integrate with the existing web APIs as much as possible, handeling user events is no different, use a function to the event callback.

In the following example we listen for onclick events for a button, and increment the value of the n signal.

h('button', {onClick:e => n(i => i + 1)}, n)

Be adviced, the event handler MUST always have one argument even if this is not being used, lest HyperScript confuses it for a signal and ignores the events.

// Ok
h('button', {onClick: e => console.log('Ok')})
h('button', {onClick: _ => console.log('Ok')})
// This event handler will be ignored
h('button', {onClick: () => console.log('Wrong')})

Reactivity

Signals

A signal is an object that holds a value with a setter to update this value and a getter that returns this value whenever it is updated.

Create signal.

// Create a signal with value one
const n = signal(1)

// Get value from signal
n()

// Set value for signal
n(2)

// Set value with an update function
n(i=>i+1)

// Derived signal
const n2 = () => n() * 2

Effects

The effect function lets you subscribe to signals and perform side-effects whenever the signal chages.

const a = signal(1), b = signal(2)
effect(() => console.log('a+b', a()+b()))
const c = () => a()+b()
effect(() => console.log('c', c()))
a(i => i+1)

Memo

The memo function caches the result of the function passed to it.

const n2 = memo(() => n() * 2)

Untrack

Conditional Rendering

h(Show, {when:() => n()%2 === 0, fallback:'odd'}, 'even')

It is also possible to conditionally render a component by prefixing it with a JavaScript and-expression, like in the example below, but using Show is going to be faster.

h('',show&&'Hi')

Rendering Lists

h(List, {each:['a','b','c']}, (v,i)=>`${i()}:${v()}`)

Fetching Resources

The resource() function lets you define a asynchronous signal.

resource(async ()=>getPosts())

Dependency Injection

Prelude supports dependency injection with the contect and useContext APIs.

const CounterCtx = context()
const useCounter = () => useContext(CounterCtx)

function CounterProvider(props) {
  const count = signal(0)
  const increment = () => count(i=>i+1)
  return h(CounterCtx.Provider, {value:[count,increment]}, props.children)
}

function Counter() {
  const [n, increment] = useCounter()
  return h('button', {onClick:e=>increment()}, n)
}

function

function App() {
  h(CounterProvider, h(Counter))
}

Router

h(Router,[
  {path:'/', component:Posts},
  {path:'/user', component:Users}
])

Learn More

  • API Reference
  • SolidJS Docs: The documentation of SolidJS also a good place for background information because Prelude is lacking extensive documentation at this time. Prelude started as a SolidJS clone, but with better HyperScript support. A lot of the concepts are the same but naming conventions can vary.

TODO

  • tailwind styling
  • SSR
  • Hydration
  • Components
    • Select
    • Multiselect
    • DataTable
    • Dropdown
    • Dialog
    • Dynamic

Links

About

A signal-based web library that lets you develop in a familiar functional style

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published