Skip to content

Commit

Permalink
docs: add docs about routing
Browse files Browse the repository at this point in the history
  • Loading branch information
aralroca committed Oct 3, 2023
1 parent 0195407 commit 3609e29
Show file tree
Hide file tree
Showing 9 changed files with 445 additions and 7 deletions.
14 changes: 8 additions & 6 deletions docs/01-getting-started/02-project-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ This page provides an overview of the file and folder structure of a Brisa proje

| | |
| ------------------------------------------------------------------------ | ---------------------------------- |
| [`pages`](/docs/pages/building-your-application/routing#pages) | Pages Router |
| [`api`](/docs/pages/building-your-application/routing#api) | Api Router |
| [`public`](/docs/app/building-your-application/optimizing/static-assets) | Static assets to be served |
| [`middleware`](/docs/app/building-your-application/configuring/middleware) | Middleware |
| [`layout`](/docs/app/building-your-application/configuring/layout) | Layout / Layouts |
| [`websocket`](/docs/app/building-your-application/configuring/websocket) | Websocket |
| [`pages`](/docs/building-your-application/routing#pages) | Pages Router |
| [`api`](/docs/building-your-application/routing#api) | Api Router |
| [`public`](/docs/building-your-application/optimizing/static-assets) | Static assets to be served |
| [`middleware`](/docs/building-your-application/configuring/middleware) | Middleware |
| [`layout`](/docs/building-your-application/configuring/layout) | Layout / Layouts |
| [`websocket`](/docs/building-your-application/configuring/websocket) | Websocket |
| [`i18n`](/docs/building-your-application/configuring/i18n) | Internationalization routing & translations |



## Top-level files
Expand Down
1 change: 0 additions & 1 deletion docs/02-app/02-layouts.md

This file was deleted.

110 changes: 110 additions & 0 deletions docs/02-building-your-application/01-routing/01-pages-and-layouts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: Pages and Layouts
description: Create your first page and shared layout with the Pages Router.
---

The Pages Router has a file-system based router built on the concept of pages (like Next.js pages folder).

When a file is added to the `pages` directory, it's automatically available as a route.

In Brisa framework, a **page** is a [Brisa Component](/docs/components-details) exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the `pages` directory. Each page is associated with a route based on its file name.

**Example**: If you create `pages/about.js` that exports a Brisa component like below, it will be accessible at `/about`.

```jsx
export default function About() {
return <div>About</div>
}
```

See the difference between React Components and Brisa Components [here](/docs/components-details).

## Index routes

The router will automatically route files named `index` to the root of the directory.

- `pages/index.js``/`
- `pages/blog/index.js``/blog`

## Nested routes

The router supports nested files. If you create a nested folder structure, files will automatically be routed in the same way still.

- `pages/blog/first-post.js``/blog/first-post`
- `pages/dashboard/settings/username.js``/dashboard/settings/username`

## Pages with Dynamic Routes

Brisa supports pages with dynamic routes. For example, if you create a file called `pages/posts/[id].js`, then it will be accessible at `posts/1`, `posts/2`, etc.

> To learn more about dynamic routing, check the [Dynamic Routing documentation](/docs/building-your-application/routing/dynamic-routes).
## Layout

The global layout is defined inside `/src/layout/index`. By default Brisa supports a default layout, but you can modify it here.


```jsx filename="src/layout/index.js"
import { RequestContext } from "brisa";

export default function Layout({ children }: { children: JSX.Element }, { route }: RequestContext) {
return (
<html>
<head>
<title id="title">My layout</title>
<link rel="icon" href="favicon.ico" />
</head>
<body>
{children}
</body>
</html>
)
}
```

It must have the same structure: `html`, `head` and `body`. If for example you forget to put the `head`, you may have issues and you will be alerted with an error during development.

All the components of Brisa (pages and layouts included), apart from the props, receive a second argument which is the **context of the request**, apart from having access to the request, you have access to a series of extra information such as the **route** of the page. In the layouts, having access to the page route is very useful to **create different layouts**.

### Example of multi-layouts

```tsx filename="src/layout/index.js"
import { type RequestContext } from "brisa";
import UserLayout from './user-layout'
import GlobalLayout from './global-layout'

export default function Layout({ children }: { children: JSX.Element }, { route }: RequestContext) {
// pathname: /en/user/aralroca/settings or /es/usuario/pepe
if(route.name.startsWith('/user/[username]')) {
return <UserLayout>{children}<UserLayout>
}

return <GlobalLayout>{children}</GlobalLayout>
}
```

## Data Fetching

Inside your layout, you can fetch data directly with `fetch`, in the same way that you can do it in pages:

```jsx filename="src/layout/index.js"
import { RequestContext } from "brisa";

export default async function Layout({ children }: { children: JSX.Element }, { route }: RequestContext) {
const data = await fetch(/* data url */).then(r => r.json());

return (
<html>
<head>
<title id="title">My layout</title>
<link rel="icon" href="favicon.ico" />
</head>
<body>
{children}
</body>
</html>
)
}
```

The `fetch` is directly native and has no wrapper to control the cache. We recommend that you do not do the same `fetch` in several places, but use the [`context`](/docs/building-your-application/data-fetching/request-context) to store the data and consume it from any component.
57 changes: 57 additions & 0 deletions docs/02-building-your-application/01-routing/02-dynamic-routes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: Dynamic Routes
description: Dynamic Routes are pages that allow you to add custom params to your URLs. Start creating Dynamic Routes and learn more here.
---

When you don't know the exact segment names ahead of time and want to create routes from dynamic data, you can use Dynamic Segments that are filled in at request time.

## Convention

A Dynamic Segment can be created by wrapping a folder's name in square brackets: `[folderName]`. For example, `[id]` or `[slug]`.

Dynamic Segments can be accessed from [`request context`](/docs/building-your-application/data-fetching/request-context).

## Example

For example, a blog could include the following route `src/pages/blog/[slug].js` where `[slug]` is the Dynamic Segment for blog posts.

```jsx
export default function Page(props, { route }) {
return <p>Post: {route.query.slug}</p>
}
```

All components can access `route.query` in the same way, not just pages.

| Route | Example URL | `params` |
| ---------------------- | ----------- | --------------- |
| `pages/blog/[slug].js` | `/blog/a` | `{ slug: 'a' }` |
| `pages/blog/[slug].js` | `/blog/b` | `{ slug: 'b' }` |
| `pages/blog/[slug].js` | `/blog/c` | `{ slug: 'c' }` |

## Catch-all Segments

Dynamic Segments can be extended to **catch-all** subsequent segments by adding an ellipsis inside the brackets `[...folderName]`.

For example, `pages/shop/[...slug].js` will match `/shop/clothes`, but also `/shop/clothes/tops`, `/shop/clothes/tops/t-shirts`, and so on.

| Route | Example URL | `params` |
| ------------------------- | ------------- | --------------------------- |
| `pages/shop/[...slug].js` | `/shop/a` | `{ slug: ['a'] }` |
| `pages/shop/[...slug].js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` |
| `pages/shop/[...slug].js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` |

## Optional Catch-all Segments

Catch-all Segments can be made **optional** by including the parameter in double square brackets: `[[...folderName]]`.

For example, `pages/shop/[[...slug]].js` will **also** match `/shop`, in addition to `/shop/clothes`, `/shop/clothes/tops`, `/shop/clothes/tops/t-shirts`.

The difference between **catch-all** and **optional catch-all** segments is that with optional, the route without the parameter is also matched (`/shop` in the example above).

| Route | Example URL | `params` |
| --------------------------- | ------------- | --------------------------- |
| `pages/shop/[[...slug]].js` | `/shop` | `{}` |
| `pages/shop/[[...slug]].js` | `/shop/a` | `{ slug: ['a'] }` |
| `pages/shop/[[...slug]].js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` |
| `pages/shop/[[...slug]].js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` |
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
title: Linking and Navigating
description: Learn how navigation works in Brisa.
---

Brisa works with MPA, so we will use the native HTML navigation and you can use the `a` tag directly:

```jsx
export default function Home() {
return (
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/about">About Us</a>
</li>
<li>
<a href="/blog/hello-world">Blog Post</a>
</li>
</ul>
)
}
```

The example above uses multiple `a` tags. Each one maps a path (`href`) to a known page:

- `/``src/pages/index.js`
- `/about``src/pages/about.js`
- `/blog/hello-world``src/pages/blog/[slug].js`


## Navigation to dynamic paths

You can also use interpolation to create the path, which comes in handy for [dynamic route segments](/docs/building-your-application/routing/dynamic-routes). For example, to show a list of posts which have been passed to the component as a prop:

```jsx
export default function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>
<a href={`/blog/${encodeURIComponent(post.slug)}`}>
{post.title}
</a>
</li>
))}
</ul>
)
}
```

> [`encodeURIComponent`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) is used in the example to keep the path utf-8 compatible.

## I18n navigation

If you have [i18n routing](/docs/routing/internationalization) enabled, during navigation you always have to forget about route translations and during the render of the page will be translated to correct translated page.

```jsx
export default function Home() {
return <a href="/about">About Us</a>
}
```

In English:

- `/about``/en/about``src/pages/about.js`

In Spanish:

- `/about``/es/sobre-nosotros``src/pages/about.js`

### Navigate to another locale

It is always possible to force a specific route in case you want to change the locale to another one:

```jsx
export default function Home() {
return <a href="/es/sobre-nosotros">About Us in Spanish</a>
}
```

## Imperative navigation

TODO
44 changes: 44 additions & 0 deletions docs/02-building-your-application/01-routing/04-custom-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Custom Errors
description: Override and extend the built-in Error page to handle custom errors.
---

## 404 Page

To create a custom 404 page you can create a `src/pages/_404.js` file.

```jsx filename="src/pages/_404.js"
export default function Custom404() {
return <h1>404 - Page Not Found</h1>
}
```

> **Good to know**: In this page you can access to the `request context`, `fetch` data, change the `head` content (meta tags, etc), and change the `response headers`, in the same way of the rest of pages.

## 500 Page

To customize the 500 page you can create a `src/pages/_500.js` file.

```jsx filename="src/pages/_500.js"
export default function Custom500({ error }, requestContext) {
return <h1>500 - {error.message}</h1>
}
```

> **Good to know**: In this page you can access to the `request context`, `fetch` data, change the `head` content (meta tags, etc), and change the `response headers`, in the same way of the rest of pages.

### Errors in component-level

If you want to control errors at the component level instead of displaying a whole new page with the error, you can make the components have the error extension by adding the `ComponentName.error`:

```jsx
export default function SomeComponent() {
return /* some JSX */
}

SomeComponent.error = ({ error }, requestContext) => {
return <p>Oops! {error.message}</p>
}
```
Loading

0 comments on commit 3609e29

Please sign in to comment.