---
title: "Your First Web Page · RUAL Documentation"
description: "Publish a web page in five minutes: add a web_page block, connect web_render, write the template, pass your data and open the URL."
canonical: https://docs.rual.nl/web/first-page
language: en
---

# Your First Web Page

Five minutes: make an empty blueprint, write a [`web_page`](https://docs.rual.nl/block-types/web/web_page) with a URL and the `*public` scope and a [`web_render`](https://docs.rual.nl/block-types/web/web_render) with a template, a title and a description, save, activate the blueprint and open the URL.

You need access to a cluster (see [Getting Access](https://docs.rual.nl/cluster/getting-access)) and the rights to edit blueprints. You work in the [Studio Code View](https://docs.rual.nl/engine/studio-code-view), because that is where a web page's URL is set today.

## Paste, save, activate, open

- Create an empty blueprint

Open the blueprints overview in RUAL Studio and create a new blueprint. Type a name and press `Next step`. Studio asks *What type of blueprint would you like*. It has no web page type yet, and `Build a UI Page` seeds a [`state_page`](https://docs.rual.nl/block-types/state%20ui/state_page), the deprecated way to build a page. Close the dialog with the `×` in its corner. You now have an empty canvas.

- Switch to Code

Use the `Canvas` | `Code` switch in the blueprint header. When your header has no such switch, your Studio does not include the code view yet.

- Paste the source

Replace the text in the editor with this. The next section explains each line.

```
blueprint "Hello page"

flow page = web_page with {event: "hello", scopes: ["*public"]} {
  web_render(
    connection: page.connection,
    template: "<main><h1>{{title}}</h1><p>{{greeting}}, this page was rendered on the server.</p></main>",
    title: "Hello",
    description: "My first RUAL web page.",
    greeting: "Welcome"
  ) with {custom_pins: [{id: "greeting", state: "greeting", type: "value"}]}
}
```

- Check

Press `Check` (`Cmd/Ctrl + Enter`). It should say *Compiles cleanly.*

- Stage to canvas

Press `Stage to canvas` (`Cmd/Ctrl + S`), then switch back to `Canvas` to see the blocks wired together.

- Save

Press `Save` in the bottom bar, or `Cmd/Ctrl + S`. Saving is what publishes: from that moment visitors get the new version.

- Activate the blueprint

Pages are inactive by default. The switch at the right of the top bar reads `Inactive`: click it. A new blueprint needs this once, and a page holds its URL only while its blueprint is active.

- Open the URL

Open `https://your-cluster/hello` and view the page's source. Your template is there, inside a complete document.

## What each line of the source says

- **`event` is the URL** without the leading slash. `hello` is `/hello`, `/` is the home page, and a segment like `products/:slug` arrives on the block's `params` out-pin.

- **`scopes` says who may open it.** `*public` is everyone. A page with no scopes at all is closed: it opens for developers only, and other visitors are sent to sign in.

- **[`web_render`](https://docs.rual.nl/block-types/web/web_render) answers the request.** Its `connection` comes from the page, and the flow reaches it from the page's flow output.

- **`template` is HTML with `{{mustache}}` values.** A short one fits on one line. For anything longer, [store the template](https://docs.rual.nl/web/layouts-and-partials) and set this pin to `{{> its-name}}`. A template can also live on the block itself, in its `html` setting, which is used when the pin is empty.

- **`title` and `description`** fill the head of the page for you, and the template can print them too.

- **`greeting` is a custom pin:** a value of your own, listed under `custom_pins`. A custom pin named `greeting` is `{{greeting}}` in the template. Wire in what you need, such as a storage result. To hand over a whole object at once, wire it into the `data` pin: its fields become variables by name.

> **Building on the canvas instead?** Right-click the canvas, choose `Add new block`, search for `web page` and pick `new web page`. Studio places a `web render` and a `current request` block with it. Drag out of the `template`, `title` and `description` pins to add a `value` block for each, and press `+ pin` on the render block for a custom pin. The one thing the canvas cannot set yet is the URL: Studio has no URL button on this block, so add `event` and `scopes` in the code view.

## The browser receives a complete document

Without a layout the page gets a plain HTML5 document around it, and the head tags are written for you from the pins:

```
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello</title>
<meta name="description" content="My first RUAL web page.">
<link rel="canonical" href="https://your-cluster/hello">
<meta property="og:description" content="My first RUAL web page.">
<meta property="og:locale" content="en_US">
<meta property="og:title" content="Hello">
<meta property="og:type" content="website">
<meta property="og:url" content="https://your-cluster/hello">
<meta name="twitter:card" content="summary">
<script src="/static-chunks/rual-web-XXXXXXXXXX.js" nonce="..." defer></script>
<script type="speculationrules" nonce="...">...</script>
</head>
<body>
<main><h1>Hello</h1><p>Welcome, this page was rendered on the server.</p></main>
</body>
</html>
```

Start the template with ` ` to write the whole document yourself, or name a [layout](https://docs.rual.nl/web/layouts-and-partials). The head tags are written for you in every case.

## When it does not work

| You see | Why | Do this |
| --- | --- | --- |
| A login page, or a redirect to `/login` | The page has no `*public` scope, or no active page holds this URL yet. | Check `scopes` and `event`, and check that the switch reads `Active`. |
| A not found page while you are signed in | No active page holds this URL. | Did you save, and does the switch read `Active`? A page of an inactive blueprint holds no URL. |
| `you do not have access to this page` | You are signed in, without a scope the page asks for. | Add the scope to your user, or to the page. |
| `this page's flow does not reach a web render block` | The flow ended without a [`web_render`](https://docs.rual.nl/block-types/web/web_render), a [`web_redirect`](https://docs.rual.nl/block-types/web/web_redirect) or an `httpconnection` answer. | Connect the flow output of the page to the render block. |
| `This page could not be rendered` | The template does not compile, or there is no template. | Open the page signed in as a blueprint developer: the reason is shown to you, and only to you. |
| A value stays empty | The name in `{{...}}` does not match a custom pin or a field of `data`. | A missing value prints nothing and is not an error. Compare the names. |

## Next steps

- [Tutorial: A Small Webshop](https://docs.rual.nl/web/build-a-shop): Storages, lists, a product page, live search, a cart and a checkout.

- [Template Syntax](https://docs.rual.nl/web/templates): Conditions, loops, filters and the variables a template sees.

- [Actions](https://docs.rual.nl/web/actions): Make a button change one row without reloading the page.

## Frequently asked

**How do I make a public web page in RUAL?**

Add a web_page block and set its event field to the URL without the leading slash, for example shop. Give it the scope *public, connect its flow to a web_render block, write the template there, and activate the blueprint.

**Why does my RUAL web page answer a 500?**

A page flow that ends without an answer is a 500 that says so. Check that the flow reaches web_render, or answers another way through an httpconnection block or web_redirect, and read the message if you are signed in as a blueprint developer: visitors only see that it failed.
