pyreon

Installation

Install the core packages:

npm install @pyreon/core @pyreon/reactivity @pyreon/runtime-dom @pyreon/vite-plugin
bun add @pyreon/core @pyreon/reactivity @pyreon/runtime-dom @pyreon/vite-plugin
pnpm add @pyreon/core @pyreon/reactivity @pyreon/runtime-dom @pyreon/vite-plugin
yarn add @pyreon/core @pyreon/reactivity @pyreon/runtime-dom @pyreon/vite-plugin

Vite Setup

Add the Pyreon plugin to your Vite config. The plugin is a default export — convention is to name the import pyreon:

import { defineConfig } from 'vite'
import pyreon from '@pyreon/vite-plugin'

export default defineConfig({
  plugins: [pyreon()],
})

Your First Component

import { signal, computed } from '@pyreon/reactivity'
import { Show } from '@pyreon/core'
import { mount } from '@pyreon/runtime-dom'

const count = signal(0)

function App() {
  const doubled = computed(() => count() * 2)

  return (
    <div>
      <h1>Hello Pyreon!</h1>
      <button onClick={() => count.update((n) => n + 1)}>
        Clicks: {count()}
      </button>
      <p>Doubled: {doubled()}</p>
      <Show when={() => count() > 0}>
        <p>You've started clicking!</p>
      </Show>
    </div>
  )
}

mount(<App />, document.getElementById('app')!)

HTML Entry Point

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Pyreon App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

Adding SSR

For server-side rendering, install the server runtime:

npm install @pyreon/runtime-server
bun add @pyreon/runtime-server
pnpm add @pyreon/runtime-server
yarn add @pyreon/runtime-server
import { renderToString } from '@pyreon/runtime-server'
import App from './App'

export async function render() {
  return await renderToString(<App />)
}

For streaming SSR with Suspense support:

import { renderToStream } from '@pyreon/runtime-server'
import App from './App'

export function render(res: WritableStream) {
  return renderToStream(<App />, res)
}

Adding Routing

npm install @pyreon/router
bun add @pyreon/router
pnpm add @pyreon/router
yarn add @pyreon/router
import { createRouter } from '@pyreon/router'

export const router = createRouter({
  routes: [
    { path: '/', component: () => import('./pages/Home') },
    { path: '/about', component: () => import('./pages/About') },
    { path: '/users/:id', component: () => import('./pages/User') },
  ],
})
import { RouterProvider, RouterView, RouterLink } from '@pyreon/router'
import { router } from './router'

export default function App() {
  return (
    <RouterProvider router={router}>
      <nav>
        <RouterLink to="/">Home</RouterLink>
        <RouterLink to="/about">About</RouterLink>
      </nav>
      <RouterView />
    </RouterProvider>
  )
}

Using a Compatibility Layer

If you're coming from React, you can use familiar hooks:

npm install @pyreon/react-compat
bun add @pyreon/react-compat
pnpm add @pyreon/react-compat
yarn add @pyreon/react-compat
import { useState, useEffect, memo } from '@pyreon/react-compat'

const Counter = memo(() => {
  const [count, setCount] = useState(0)

  useEffect(() => {
    document.title = `Count: ${count}`
  }, [count])

  return <button onClick={() => setCount(count + 1)}>Count: {count}</button>
})

What's Next?

Getting Started