Skip to content

Commit 43648ca

Browse files
docs: update canary docs for Fresh 2
There are still some rough edges here and there, but it's in a much better shape than what we have today.
1 parent a66e0df commit 43648ca

File tree

29 files changed

+2094
-62
lines changed

29 files changed

+2094
-62
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
description: |
3+
Add a global app wrapper to provide common meta tags or context for application routes.
4+
---
5+
6+
The app wrapper component is a Preact component that represents the outer
7+
structure of the HTML document, typically up until the `<body>`-tag. It is only
8+
rendered on the server and never on the client. The passed `Component` value
9+
represents the children of this component.
10+
11+
```tsx
12+
function AppWrapper({ Component }) {
13+
return (
14+
<html lang="en">
15+
<head>
16+
<meta charset="utf-8" />
17+
<title>My App</title>
18+
</head>
19+
<body>
20+
<Component />
21+
</body>
22+
</html>
23+
);
24+
}
25+
26+
app.appWrapper(AppWrapper);
27+
```
28+
29+
Every `ctx.render()` call will include the app wrapper component by default,
30+
unless opted out.
31+
32+
Note that only one app wrapper component is supported per
33+
[`App`](/docs/canary/concepts/app) instance.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
description: |
3+
Error pages can be used to customize the page that is shown when an error occurs in the application.
4+
---
5+
6+
Fresh supports customizing the `404 Not Found`, and the
7+
`500 Internal Server Error` pages. These are shown when a request is made but no
8+
matching route exists, and when a middleware, route handler, or page component
9+
throws an error respectively.
10+
11+
### 404: Not Found
12+
13+
The 404 page can be customized by creating a `_404.tsx` file in the `routes/`
14+
folder. The file must have a default export that is a regular Preact component.
15+
A props object of type `PageProps` is passed in as an argument.
16+
17+
```tsx routes/_404.tsx
18+
import { PageProps } from "$fresh/server.ts";
19+
20+
export default function NotFoundPage({ url }: PageProps) {
21+
return <p>404 not found: {url.pathname}</p>;
22+
}
23+
```
24+
25+
#### Manually render 404 pages
26+
27+
The `_404.tsx` file will be invoked automatically when no route matches the URL.
28+
In some cases, one needs to manually trigger the rendering of the 404 page, for
29+
example when the route did match, but the requested resource does not exist.
30+
This can be achieved with `ctx.renderNotFound`.
31+
32+
```tsx routes/blog/[slug].tsx
33+
import { Handlers, PageProps } from "$fresh/server.ts";
34+
35+
export const handler: Handlers = {
36+
async GET(req, ctx) {
37+
const blogpost = await fetchBlogpost(ctx.params.slug);
38+
if (!blogpost) {
39+
return ctx.renderNotFound({
40+
custom: "prop",
41+
});
42+
}
43+
return ctx.render({ blogpost });
44+
},
45+
};
46+
47+
export default function BlogpostPage({ data }) {
48+
return (
49+
<article>
50+
<h1>{data.blogpost.title}</h1>
51+
{/* rest of your page */}
52+
</article>
53+
);
54+
}
55+
```
56+
57+
This can also be achieved by throwing an error, if you're uninterested in
58+
passing specific data to your 404 page:
59+
60+
```tsx
61+
import { Handlers } from "$fresh/server.ts";
62+
63+
export const handler: Handlers = {
64+
GET(_req, _ctx) {
65+
throw new Deno.errors.NotFound();
66+
},
67+
};
68+
```
69+
70+
### 500: Internal Server Error
71+
72+
The 500 page can be customized by creating a `_500.tsx` file in the `routes/`
73+
folder. The file must have a default export that is a regular Preact component.
74+
A props object of type `PageProps` is passed in as an argument.
75+
76+
```tsx routes/_500.tsx
77+
import { PageProps } from "$fresh/server.ts";
78+
79+
export default function Error500Page({ error }: PageProps) {
80+
return <p>500 internal error: {(error as Error).message}</p>;
81+
}
82+
```

docs/canary/advanced/forms.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
---
2+
description: |
3+
Robustly handle user inputs using HTML `<form>` elements client side, and form
4+
submission handlers server side.
5+
---
6+
7+
For stronger resiliency and user experience, Fresh relies on native browser
8+
support for form submissions with the HTML `<form>` element.
9+
10+
In the browser, a `<form>` submit will send an HTML action (usually `GET` or
11+
`POST`) to the server, which responds with a new page to render.
12+
13+
## POST request with `application/x-www-form-urlencoded`
14+
15+
Forms typically submit as a `GET` request with data encoded in the URL's search
16+
parameters, or as a `POST` request with either an
17+
`application/x-www-form-urlencoded` or `multipart/form-data` body.
18+
19+
This example demonstrates how to handle `application/x-www-form-urlencoded`
20+
`<form>` submissions:
21+
22+
```tsx routes/subscribe.tsx
23+
import { Handlers } from "$fresh/server.ts";
24+
25+
export const handler: Handlers = {
26+
async GET(req, ctx) {
27+
return await ctx.render();
28+
},
29+
async POST(req, ctx) {
30+
const form = await req.formData();
31+
const email = form.get("email")?.toString();
32+
33+
// Add email to list.
34+
35+
// Redirect user to thank you page.
36+
const headers = new Headers();
37+
headers.set("location", "/thanks-for-subscribing");
38+
return new Response(null, {
39+
status: 303, // See Other
40+
headers,
41+
});
42+
},
43+
};
44+
45+
export default function Subscribe() {
46+
return (
47+
<>
48+
<form method="post">
49+
<input type="email" name="email" value="" />
50+
<button type="submit">Subscribe</button>
51+
</form>
52+
</>
53+
);
54+
}
55+
```
56+
57+
When the user submits the form, Deno will retrieve the `email` value using the
58+
request's `formData()` method, add the email to a list, and redirect the user to
59+
a thank you page.
60+
61+
## Handling file uploads
62+
63+
File uploads can be handled in a very similar manner to the example above. Note
64+
that this time, we have to explicitly declare the form's encoding to be
65+
`multipart/form-data`.
66+
67+
```tsx routes/subscribe.tsx
68+
import { Handlers, type PageProps } from "$fresh/server.ts";
69+
70+
interface Props {
71+
message: string | null;
72+
}
73+
74+
export const handler: Handlers<Props> = {
75+
async GET(req, ctx) {
76+
return await ctx.render({
77+
message: null,
78+
});
79+
},
80+
async POST(req, ctx) {
81+
const form = await req.formData();
82+
const file = form.get("my-file") as File;
83+
84+
if (!file) {
85+
return ctx.render({
86+
message: `Please try again`,
87+
});
88+
}
89+
90+
const name = file.name;
91+
const contents = await file.text();
92+
93+
console.log(contents);
94+
95+
return ctx.render({
96+
message: `${name} uploaded!`,
97+
});
98+
},
99+
};
100+
101+
export default function Upload(props: PageProps<Props>) {
102+
const { message } = props.data;
103+
return (
104+
<>
105+
<form method="post" encType="multipart/form-data">
106+
<input type="file" name="my-file" />
107+
<button type="submit">Upload</button>
108+
</form>
109+
{message ? <p>{message}</p> : null}
110+
</>
111+
);
112+
}
113+
```
114+
115+
## A note of caution
116+
117+
These examples are simplified to demonstrate how Deno and Fresh handle HTTP
118+
requests. In the Real World™, you'll want to validate your data (_especially the
119+
file type_) and protect against cross-site request forgery. Consider yourself
120+
warned.

docs/canary/advanced/layouts.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
description: "Create re-usable layouts across routes"
3+
---
4+
5+
Layouts are plain Preact components that are inherited based on the matching
6+
pattern. When you have a section on your site where all pages share the same
7+
HTML structure and only the content changes, a layout is a neat way to abstract
8+
this. Layouts only ever render on the server. The passed `Component` value
9+
represents the children of this component.
10+
11+
```tsx
12+
function PageLayout({ Component }) {
13+
return (
14+
<div>
15+
<Component />
16+
<aside>Here is some sidebar content</aside>
17+
</div>
18+
);
19+
}
20+
21+
const app = new App()
22+
.layout("*", PageLayout)
23+
.get("/", (ctx) => ctx.render(<h1>hello</h1>));
24+
```
25+
26+
If you browse to the `/` route, Fresh will render the following HTML
27+
28+
```html
29+
<div>
30+
<h1>hello world</h1>
31+
<aside>Here is some sidebar content</aside>
32+
</div>
33+
```
34+
35+
## Options
36+
37+
Add a layout and ignore all previously inherited ones.
38+
39+
```ts
40+
app.layout("/foo/bar", MyComponent, { skipInheritedLayouts: true });
41+
```
42+
43+
Ignore the app wrapper component:
44+
45+
```ts
46+
app.layout("/foo/bar", MyComponent, { skipAppWrapper: true });
47+
```

0 commit comments

Comments
 (0)