aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorHayleigh Thompson <me@hayleigh.dev>2024-02-26 20:21:41 +0000
committerHayleigh Thompson <me@hayleigh.dev>2024-02-26 20:21:41 +0000
commit01e0b22e5e0f0534d68efc2f25befae7172d522d (patch)
tree7cf7c8c40f54d95f29b21afface5aa8b84760342 /docs
parent47028efa57348d92bbd43a3ff2eb32c1d7e29766 (diff)
downloadlustre-01e0b22e5e0f0534d68efc2f25befae7172d522d.tar.gz
lustre-01e0b22e5e0f0534d68efc2f25befae7172d522d.zip
:memo Move docs bages into pages/ dir.
Diffstat (limited to 'docs')
-rw-r--r--docs/guide/01-quickstart.md384
1 files changed, 0 insertions, 384 deletions
diff --git a/docs/guide/01-quickstart.md b/docs/guide/01-quickstart.md
deleted file mode 100644
index b0b61fb..0000000
--- a/docs/guide/01-quickstart.md
+++ /dev/null
@@ -1,384 +0,0 @@
-# 01 Quickstart Guide
-
-Welcome to the Lustre quickstart guide! This document should get you up to speed
-with the core ideas that underpin every Lustre application as well as how to get
-something on the screen.
-
-## What is a SPA?
-
-Lustre can be used to create HTML in many different contexts, but it is primarily
-designed to be used to build Single-Page Applications – or SPAs. SPAs are a type
-of Web application that render content primarily in the browser (rather than on
-the server) and, crucially, do not require a full page load when navigating
-between pages or loading new content.
-
-To help build these kinds of applications, Lustre comes with an opinionated
-runtime. Some of Lustre's core features include:
-
-- **Declarative rendering**: User interfaces are constructed using a declarative
- API that describes HTML as a function of your application's state. This is in
- contrast to more traditional imperative approaches to direct DOM mutation like
- jQuery.
-
-- **State management**: If UIs are a function of state, then orchestrating state
- changes is crucial! Lustre provides a simple message-based state management
- system modelled after OTP [gen_servers](https://www.erlang.org/doc/design_principles/gen_server_concepts),
- Gleam's [actors](https://hexdocs.pm/gleam_otp/gleam/otp/actor.html), and the
- [Elm Architecture](https://guide.elm-lang.org/architecture/).
-
-- **Managed side effects**: Managing asynchronous operations like HTTP requests
- and timers can be tricky when JavaScript is single-threaded. Lustre provides a
- runtime to manage these side effects and let them communicate with your application
- using the same messages as your update loop.
-
-## Your first Lustre program
-
-To get started, let's create a new Gleam application and add Lustre as a dependency.
-
-```sh
-$ gleam new app && cd app && gleam add lustre
-```
-
-By default, Gleam builds projects for the Erlang target unless told otherwise. We
-can change this by adding a `target` field to the `gleam.toml` file generated in
-the root of the project.
-
-```diff
- name = "app"
-+ target = "javascript"
- version = "1.0.0"
-
- ...
-```
-
-The simplest type of Lustre application is constructed with the `element` function.
-This produces an application that renders a static piece of content without the
-typical update loop.
-
-We can start by importing `lustre` and `lustre/element` and just rendering some
-text:
-
-```gleam
-import lustre
-import lustre/element
-
-pub fn main() {
- lustre.element(element.text("Hello, world!"))
-}
-```
-
-Lustre includes tooling like a server to serve your application in development.
-You can start that server by running:
-
-```sh
-$ gleam run -m lustre dev
-```
-
-The first time you run this command might take a little while, but subsequent runs
-should be much faster!
-
-> **Note**: Lustre uses esbuild under the hood, and attempts to download the [right
-> binary for your platform](https://esbuild.github.io/getting-started/#download-a-build).
-> If you're not connected to the internet, on an unsupported platform, or don't
-> want Lustre to download the binary you can grab or build it yourself and place it
-> in `build/.lustre/bin/esbuild`.
-
-Once the server is up and running you should be able to visit http://localhost:1234
-and be greeted with your "Hello, world!" message.
-
-We mentioned Lustre has a declarative API for constructing HTML. Let's see what
-that looks like by building something slightly more complex.
-
-```gleam
-import lustre
-import lustre/attribute
-import lustre/element
-import lustre/element/html
-
-pub fn main() {
- lustre.element(
- html.div([], [
- html.h1([], [element.text("Hello, world!")]),
- html.figure([], [
- html.img([attribute.src("https://cataas.com/cat")]),
- html.figcaption([], [element.text("A cat!")])
- ])
- ])
- )
-}
-```
-
-Here we _describe_ the structure of the HTML we want to render, and leave the
-busywork to Lustre's runtime: that's what makes it declarative!
-
-"**Where are the templates?**" we hear you cry. Lustre doesn't have a separate
-templating syntax like JSX or HEEx for a few reasons (lack of metaprogramming
-built into Gleam, for one). Some folks might find this a bit odd at first, but
-we encourage you to give it a try. Realising that your UI is _just functions_
-can be a bit of a lightbulb moment as you build more complex applications.
-
-## Adding interactivity
-
-Rendering static HTML is great, but we said at the beginning Lustre was designed
-primarily for building SPAs – and SPAs are interactive! To do that we'll need
-to move on from `lustre.element` to the first of Lustre's application constructors
-that includes an update loop: `lustre.simple`.
-
-```gleam
-import gleam/int
-import lustre
-import lustre/element
-import lustre/element/html
-import lustre/event
-
-pub fn main() {
- lustre.simple(init, update, view)
-}
-```
-
-There are three main building blocks to every interactive Lustre application:
-
-- A `Model` that represents your application's state and an `init` function
- to create it.
-
-- A `Msg` type that represents all the different ways the outside world can
- communicate with your application and an `update` function that modifies
- your model in response to those messages.
-
-- A `view` function that renders your model to HTML.
-
-We'll build a simple counter application to demonstrate these concepts. Our
-model can be an `Int` and our `init` function will initialise it to `0`:
-
-```gleam
-pub type Model = Int
-
-fn init(_) -> Model {
- 0
-}
-```
-
-> **Note**: The `init` function always takes a single argument! These are the "flags"
-> or start arguments you can pass in when your application is started with
-> `lustre.start`. For the time being, we can ignore them, but they're useful for
-> passing in configuration or other data when your application starts.
-
-The main update loop in a Lustre application revolves around messages passed in
-from the outside world. For our counter application, we'll have two messages to
-increment and decrement the counter:
-
-```gleam
-pub type Msg {
- Increment
- Decrement
-}
-
-pub fn update(model: Model, msg: Msg) -> Model {
- case msg {
- Increment -> model + 1
- Decrement -> model - 1
- }
-}
-```
-
-Each time a message is produced from an event listener, Lustre will call your
-`update` function with the current model and the incoming message. The result
-will be the new application state that is then passed to the `view` function:
-
-```gleam
-pub fn view(model: Model) -> lustre.Element(Msg) {
- let count = int.to_string(model)
-
- html.div([], [
- html.button([event.click(Increment)], [
- element.text("+")
- ]),
- element.text(count),
- html.button([event.click(Decrement)], [
- element.text("-")
- ])
- ])
-}
-```
-
-The above snippet attaches two click event listeners that produce an `Increment`
-or `Decrement` message when clicked. The Lustre runtime is responsible for
-attaching these event listeners and calling your `update` function with the
-resulting message.
-
-> **Note**: notice that the return type of `view` is `lustre.Element(Msg)`. The
-> type parameter `Msg` tells us the kinds of messages this element might produce
-> from events: type safety to the rescue!
-
-This forms the core of every Lustre application:
-
-- A model produces some view.
-- The view can produce messages in response to user interaction.
-- Those messages are passed to the update function to produce a new model.
-- ... and the cycle continues.
-
-## Talking to the outside world
-
-This "closed loop" of messages and updates works well if all we need is an
-interactive document, but many applications will also need to talk to the outside
-world – whether that's fetching data from an API, setting up a WebSocket connection,
-or even just setting a timer.
-
-Lustre manages these side effects through an abstraction called an `Effect`. In
-essence, effects are any functions that talk with the outside world and might
-want to send messages back to your application. Lustre lets you write your own
-effects, but for now we'll use a community package called
-[`lustre_http`](https://hexdocs.pm/lustre_http/index.html) to fetch a new cat image
-every time the counter is incremented.
-
-Because this is a separate package, make sure to add it to your project first.
-While we're here, we'll also add `gleam_json` so we can decode the response from
-the cat API:
-
-```sh
-$ gleam add gleam_json lustre_http
-```
-
-Now we are introducing side effects, we need to graduate from `lustre.simple` to
-the more powerful `lustre.application` constructor.
-
-```gleam
-import gleam/int
-import lustre
-import lustre_http
-import lustre/attribute
-import lustre/element
-import lustre/element/html
-import lustre/event
-
-pub fn main() {
- lustre.application(init, update, view)
-}
-```
-
-If you edited your previous counter app, you'll notice the program no longer
-compiles. Specifically, the type of our `init` and `update` functions are wrong
-for the new `lustre.application` constructor!
-
-In order to tell Lustre about what effects it should perform, these functions now
-need to return a _tuple_ of the new model and any effects. We can amend our `init`
-function like so:
-
-```gleam
-pub type Model {
- Model(count: Int, cats: List(String))
-}
-
-fn init(_) -> #(Model, effect.Effect(Msg)) {
- #(Model(0, []), effect.none())
-}
-```
-
-The `effect.none` function is a way of saying "no effects" – we don't need to do
-anything when the application starts. We've also changed our `Model` type from a
-simple type alias to a Gleam [record](https://tour.gleam.run/data-types/records/)
-that holds both the current count and a list of cat image URLs.
-
-In our `update` function, we want to fetch a new cat image every time the counter
-is incremented. To do this we need two things:
-
-- An `Effect` to describe the request the runtime should perform.
-- A variant of our `Msg` to handle the response.
-
-The `lustre_http` package has the effect side of things handled, so we just need
-to modify our `Msg` type to include a new variant for the response:
-
-```gleam
-pub type Msg {
- Increment
- Decrement
- GotCat(Result(String, lustre_http.HttpError))
-}
-```
-
-Finally, we can modify our `update` function to also fetch a cat image when the
-counter is incremented and handle the response:
-
-```gleam
-pub fn update(model: Model, msg: Msg) -> #(Model, effect.Effect(Msg)) {
- case msg {
- Increment -> #(Model(..model, count: model.count + 1), get_cat())
- Decrement -> #(Model(..model, count: model.count - 1), effect.none())
- GotCat(Ok(cat)) -> #(Model(..model, cats: [cat, ..model.cats]), effect.none())
- GotCat(Error(_)) -> #(model, effect.none())
- }
-}
-
-fn get_cat() -> effect.Effect(Msg) {
- let decoder = dynamic.field("_id", dynamic.string)
- let expect = lustre_http.expect_json(decoder, GotCat)
-
- lustre_http.get("https://cataas.com/cat?json=true", expect)
-}
-```
-
-> **Note**: The `get_cat` function returns an `Effect` that tells the runtime how
-> to fetch a cat image. It's important to know that the `get_cat` function doesn't
-> perform the request directly! This is why we need to add the `GotCat` message
-> variant: the runtime needs to know what to do with the response when it arrives.
-
-This model of managed effects can feel cumbersome at first, but it comes with some
-benefits. Forcing side effects to produce a message means our message type naturally
-describes all the ways the world can communicate with our application; as an app
-grows being able to get this kind of overview is invaluable! It also means we can
-test our update loop in isolation from the runtime and side effects: we could write
-tests that verify a particular sequence of messages produces an expected model
-without needing to mock out HTTP requests or timers.
-
-Before we forget, let's also update our `view` function to actually display the
-cat images we're fetching:
-
-```gleam
-pub fn view(model: Model) -> lustre.Element(Msg) {
- let count = int.to_string(model.count)
-
- html.div([], [
- html.button([event.click(Increment)], [
- element.text("+")
- ]),
- element.text(count),
- html.button([event.click(Decrement)], [
- element.text("-")
- ]),
- html.div([], list.map(model.cats, fn(cat) {
- html.img([attribute.src(cat)])
- }))
- ])
-}
-```
-
-## Where to go from here
-
-Believe it or not, you've already seen about 80% of what Lustre has to offer! From
-these core concepts, you can build rich interactive applications that are predictable
-and maintainable. Where to go from here depends on what you want to build, and
-how you like to learn:
-
-- There are a number of [examples](https://github.com/lustre-labs/lustre/tree/main/examples)
- if the Lustre repository that gradually introduce more complex applications
- and ideas.
-
-- The [rest of this guide](/guide/02-state-management) also continues to teach
- Lustre's high-level concepts and best-practices.
-
-- If you're coming from LiveView or have heard about Lustre's server components
- and want to learn more, you can skip to the [server components](/guide/05-server-components)
- section of the guide to learn about how to run Lustre applications on the backend.
-
-- Of course, if you want to dive in and start making things straight away, the
- [API documentation](/lustre) is always handy to keep open.
-
-## Getting help
-
-If you're having trouble with Lustre or not sure what the right way to do
-something is, the best place to get help is the [Gleam Discord server](https://discord.gg/Fm8Pwmy).
-You could also open an issue on the [Lustre GitHub repository](https://github.com/lustre-labs/lustre/issues).
-
-While our docs are still a work in progress, the official [Elm guide](https://guide.elm-lang.org)
-is also a great resource for learning about the Model-View-Update architecture
-and the kinds of patterns that Lustre is built around.