# Starfyre builds a reactive web frontend from Python files and a pyxide markup

> sparckles/starfyre is a BSD-2-Clause Python framework for writing reactive front ends without writing JavaScript, using Pyscript for the client side and a pyxide element that acts as a container for other components or plain HTML. The project ships a CLI that creates projects from a template, builds them to a distribution directory, and serves them, with file based routing, CSS files and a dependency manifest, although the state management layer is described by its own documentation as very early.

**sparckles/starfyre** — A reactive, WASM based SSR Python Web Framework for Front-End Applications

- Repository: https://github.com/sparckles/starfyre
- Website: http://sparckles.cloud/starfyre/
- Stars: 590 · Forks: 23
- Language: Python
- License: BSD-2-Clause
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/sparckles-starfyre

## One language on both sides of the wire

Starfyre is described as a library that allows you to build reactive frontends using only Python, so you can create interactive, real-time applications with minimal effort, by defining your frontend as a collection of observables and reactive functions and letting the framework handle the rest. The mechanism underneath is named in the same paragraph. Starfyre is based on Pyscript for the client side functions, and it uses the concept of pyxides when structuring code. A pyxide translates to a container, and every component is a container, which can contain other components or HTML elements. That is the whole vocabulary of the markup layer, and it is deliberately small. The point of the design is that a component is not a special object with a registration step, it is an element in the tree, and the tree is written inline in the file where the logic lives.

## Two commands to get from nothing to a running page

Installation is a normal pip install:

```bash
pip install starfyre
```

And creating a project is a single command:

```bash
starfyre --create="my-app"
```

That second command uses the create-starfyre-app repository as the template, and the README points at a hosted sample project so you can see the shape of a finished application before writing one. The sample application also lives in the repository itself, in a test_application directory, which is the fastest way to understand the file layout. The editors have support too, with a Vim plugin for syntax highlighting and a VS Code extension listed in the marketplace. Both of those are separate repositories, which tells you the framework expects to be edited in an existing tool rather than in a special environment.

## A page file mixes Python, a client block and pyxide markup

The simplest component shows the whole idea in one file, and it is worth reading closely. The file lives at my-app/pages/__init__.fyre, imports a stylesheet, defines a function, and then carries a client block:

```python
import "../styles/index.css"

def message():
  return "World"

---client
from starfyre import js

def handle_click():
  js.console.log("Hello World")
---

<pyxide>
  <div onclick={handle_click()}>
    Hello, {message()}
  </div>
</pyxide>
```

The triple dash markers delimit the client side section, which is the part that runs in the browser through Pyscript, and the js import is what gives it access to the browser console. The pyxide element below is the component itself, and the curly braces are interpolation, so message() is called to produce the text and handle_click() is bound as the click handler. The whole thing is one file, and there is no separate template language to learn beyond the pyxide tag.

## Components are files, imported with an alias for the source directory

Using a component works the way an import works, and the alias is the detail to remember. The page imports a component from the components directory, and the comment in the sample explains that the @ symbol is the alias for the source directory, in that case my-app:

```python
import "../styles/index.css"
from @.components.custom_component import custom_component
# @ is the alias for the source directory. e.g. my-app in our case

<pyxide>
 <custom_component></custom_component>
</pyxide>
```

The component itself is a file, my-app/src/components/custom_component.fyre, containing nothing more than a pyxide element. The consequence is that a component is a unit of source code with a name and a location, and the file extension is the marker that tells the compiler to treat it as one. That is a different model from the JavaScript frameworks where a component is a function registered in a tree, and it is the reason the file layout in the sample project mirrors a Python package rather than a JavaScript one.

## Signals are early, and the naming rule is the API

The state management section is the most candid part of the README, and it is worth reading before you commit to the framework. It says that signals are super early at this moment, and that you need to have the word signal when declaring a variable, giving get_signal, set_signal and use_signal as the examples. The consequence is visible in the sample, where the three functions are created together and then used:

```python
---client

[get_signal, set_signal, use_signal] = create_signal("Hello World")

def handle_click():
  set_signal("Goodbye World")
---

<pyxide>
  <div onclick={handle_click}>
    {use_signal()}
  </div>
</pyxide>
```

The limitation the README states is that use_signal and get_signal cannot be evaluated on the client, so you cannot have an expression like {use_signal()+1}, and it says this will be fixed with a better serialization. So the reactive story is real but unfinished, and the naming convention is a convention rather than a type, which means a typo in a signal name silently produces a plain variable.

## File based routing and a dependency manifest

Routing is file based, and the layout is a pages directory with one file per route:

```bash
my-app
├── pages
│   ├── __init__.fyre
│   ├── about.fyre
│   └── nav.fyre
```

Styling is handled with CSS files imported by the page and with a style block in the markup, and the import statement is the same one Python uses, so the stylesheet travels with the component that needs it. Dependencies are declared in a starfyre_config.toml file in the project root, with separate lists for client side pyxide packages and server side packages, and a js_modules table for browser modules loaded from a CDN:

```toml
pyxide_packages = [] # for client side packages
server_packages = [] # for server side packages

[js_modules]
is_odd = "https://cdn.jsdelivr.net/npm/is-odd@3.0.1/+esm"
Fireworks = "https://cdn.jsdelivr.net/npm/fireworks-js@2.10.7/+esm"
```

The CLI can edit that file for you, with --add-pyxide-package, --add-server-package and --add-js-module, and the last of those takes an --as alias so the module can be imported under a different name. A js_modules table is worth noticing, because it means a project that needs a JavaScript library can pull it in without a JavaScript build step, which is the main reason to reach for a Python front end in the first place.

## The CLI builds a directory you can open in a browser

The command line interface is described as a tool to compile and build a Starfyre project, and the usage text lists the four things it can do:

```bash
Usage: python -m starfyre [OPTIONS]

  Command-line interface to compile and build a Starfyre project.

  Args:

      path (str): Path to the project directory.

      build (bool): Whether to start the build package.

      create (str): Name of the project to create.

      serve (bool): Whether to serve the project.

Options:
  --path TEXT    Path to the project. Requires --build.
  --build        Compile and build package. Requires --path.
  --create TEXT  Create a new project. Requires a project name.
  --serve        Serve the project. Requires --path.
  --help         Show this message and exit.
```

The local development loop in the README is explicit about the order. Fork and clone the repository, install Poetry with the install.python-poetry.org script, run poetry install, enter the shell with poetry shell, and then run ./build.sh, which runs two commands sequentially, the first being python -m starfyre --build=True --path="test_application/". The output lands in the build directory, which is a Python package containing the compiled files, and the test application ends up in test_application/dist where you open index.html in a browser. The package itself is Poetry based, with click, requests and toml as the runtime dependencies, the entry point declared as starfyre = "starfyre.cli:run", and a Dockerfile that starts from python:3.10-slim-buster, installs the package with pip3 and runs ./build.sh.

## Conclusion

Adopt Starfyre if your team wants to keep a front end in Python, with reactive state, a file based router and a component model expressed in a single file format, and if you are willing to work inside an early state management layer. Do not adopt it for a front end that has to ship today, because the newest published release is v0.21.0 from 2024-01-20 even though the repository is being pushed in October 2026, and the signal handling is described as very early. Before you start, read the state management section carefully, and check the changelog and the repository for changes that have not been released to PyPI.

## FAQ

### How do I create a new Starfyre project?

Install the package with pip install starfyre and run starfyre --create="my-app", which uses the create-starfyre-app repository as the template. A sample project is also hosted on GitHub, and the repository itself contains a test_application directory you can build and open.

### What is a pyxide in Starfyre?

A pyxide translates to a container, and every component is a container, which can contain other components or HTML elements. The markup and the logic live in the same .fyre file, with a pyxide element holding the tree and curly braces interpolating values and binding handlers.

### How does state management work in Starfyre?

Through signals, which the documentation describes as very early. You need the word signal in the variable name, with get_signal, set_signal and use_signal as the examples, and the limitation stated is that use_signal and get_signal cannot be evaluated on the client, so you cannot write an expression like {use_signal()+1}. The README says this will be fixed with a better serialization.

### How do I add dependencies to a Starfyre project?

Put a starfyre_config.toml file in the project root with pyxide_packages for client side packages, server_packages for server side packages, and a js_modules table for browser modules loaded from a CDN. The CLI can edit the file for you with --add-pyxide-package, --add-server-package and --add-js-module, and the last takes an --as alias.

### How do I build and run a Starfyre project locally?

The README describes the development loop with Poetry: install the dependencies with poetry install, enter the environment with poetry shell, and run ./build.sh, which executes python -m starfyre --build=True --path="test_application/" and writes the compiled files into the build directory. The built application ends up in test_application/dist, where you open index.html in a browser.

## Sources

- [License: BSD-2-Clause](https://github.com/sparckles/starfyre/blob/main/LICENSE)
- [Project website](http://sparckles.cloud/starfyre/)
- [README](https://github.com/sparckles/starfyre/blob/main/README.md)
- [Releases](https://github.com/sparckles/starfyre/releases)
- [sparckles/starfyre on GitHub](https://github.com/sparckles/starfyre)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sparckles-starfyre
