This version of the documentation is outdated, and features documented here may work differently now. You can see the latest stable version of the docs here.

Core Principles

Perseus has two fundamental building blocks that make up its entire approach to app building, and if you understand them, you'll be able build extremely complex interfaces in Perseus that come out as fantastic apps.

  1. An app is split into templates, which are split into pages.
  2. A page is generated from a template and some state.

The first of these is based on the crucial idea of routing on the web, that everything is split into pages. This will be pretty familiar to most people: websites have different pages that display different content. Perseus extends on this with the idea of templates, which can produce pages.

The second fundamental tells us how templates work: they take in state, and they put out pages. Template + state = page.

Let's take an example to understand how this works in practice. Let's say you're building a music player app that has a vast library of songs (we'll ignore playlists, artists, etc. to keep things simple). The first step in designing your app is thinking about its structure. It comes fairly quickly that you'll need an index page to show the top songs, an about page to tell people about the platform, and one page for each song. Now, the index and about pages have different structures, but every song page has the same structure, just with different content. This is where templates come in. You would have one template for the index page and another for the about page, and then you'd have a third template for the songs pages. Unlike the other two, this template takes in state, which it can use to create many different pages with the same structure.

How we generate and manage that stage is where Perseus really shines. Are all those songs listed in a database available at build-time? Use the build state strategy. Are there too many to build all at once? Use incremental generation to build only the most commonly used songs first, and then build the rest on-demand when they're first accessed, caching to make them fast for subsequent users.

Once that state is generated, Perseus will go right ahead and proactively prerender your pages, meaning your users see content the second they load your site.

These ideas are built into Perseus at the core, and generating state for templates to generate pages is the fundamental idea behind Perseus. You'll find similar concepts in popular JavaScript frameworks like NextJS and GatsbyJS. It's Perseus' speed, ergonomics, and what it does from there that sets it apart. Once you've generated some state and you've got all the pages ready for your app, there's still a log of work to be done on this music player app. A given song might be paused or playing, the user might have manually turned off dark mode, autoplaying related songs might be on or off. This is all state, but it's not state that we can handle when we build your app. Traditionally, frameworks would leave you on your own here to work this all out, but Perseus tries to be a little more helpful by automatically making your state reactive. Let's say the state for a single song page includes the properties name, artist, album, year, and paused (there'd probably be a lot more in reality though!). The first four can be set at build time and forgotten about, but paused could be changed at any time. No problem, you can change it once the page is loaded. Just call .set() on it and Perseus will not only update it locally, but it will update it in a store global to your app so that, if a user goes back to that song later, it will be preserved (or not, your choice). And what about things like dark_mode, state that's relevant to the whole app? Well, Perseus provides inbuilt support for reactive global state that can be interacted with from any page.

Now, if you're familiar with user interface (UI) development, this might all sound familiar to you, it's very similar to the MVC, or model, view, controller pattern. If you've never heard of this, it's just a way of developing apps in which you hold all the states that your app can possibly be in in a model and use that to build a view. Then you handle user interaction with a controller, which modifies the state, and the view updates accordingly. Perseus doesn't force this structure on you, and in fact you can opt out entirely from all this reactive state business if it's not your cup of tea with no problems, because Perseus doesn't use MVC as a pattern that you develop in, it uses it as an architecture that your code works in. You can use development patterns from 1960 or 2060 if you want, Perseus doesn't mind, it'll just work away in the background and make sure your app's state just works.

Perseus also adds a little twist to the usual ideas of app state. If your entire app is represented in its state, then wouldn't it be cool if you could freeze that state, save it somewhere, and then boot it back up later to bring your app to exactly where it was before. This is inbuilt into Perseus, and it's still insanely fast. But, if you don't want it, you can turn it off, no problem.

If you like the sound of all that, keep reading, and you'll learn how to build your first Perseus app!