Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: update canary for Fresh 2 #2558

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
WIP
  • Loading branch information
marvinhagemeister committed Jul 3, 2024
commit 07f351082f5126a829dacfdb8ae5fe70d0f816c3
79 changes: 79 additions & 0 deletions docs/canary/concepts/app-wrapper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
description: |
Add a global app wrapper to provide common meta tags or context for application routes.
---

An app wrapper is defined in an `_app.tsx` file in `routes/` folder and is
typically used to create the outer structure of an HTML document. It must
contain a default export that is a regular Preact component. Only one such
wrapper is allowed per application.

The component to be wrapped is received via props, in addition to a few other
things. This allows for the introduction of a global container functioning as a
template which can be conditioned based on state and params. Note that any state
set by middleware is available via `props.state`.

```tsx routes/_app.tsx
import { define } from "../utils/state.ts";

export default define.page(function App({ Component, state }) {
// do something with state here
return (
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Fresh app</title>
</head>
<body>
<Component />
</body>
</html>
);
});
```

## Async app wrapper

Similar to routes and layouts, the app wrapper can be made asynchronous. This
changes the function signature so that the first argument is the `Request`
instance and the second one is the `FreshContext`.

```tsx routes/_app.tsx
import { define } from "../utils/state.ts";

export default define.page(async function App(props) {
const data = await somehowLoadData();

return (
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Fresh app</title>
</head>
<body>
<h1>Hello {data.name}</h1>
<props.Component />
</body>
</html>
);
});
```

## Disabling the app wrapper

Rendering the app wrapper can be skipped on a route or layout basis. To do that,
set `skipAppWrapper: true` to the layout or route config.

```tsx routes/my-special-route.tsx
import { RouteConfig } from "fresh";

export const config: RouteConfig = {
skipAppWrapper: true, // Skip the app wrapper during rendering
};

export default define.page(function Page() {
// ...
});
```
113 changes: 113 additions & 0 deletions docs/canary/concepts/forms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
description: |
Robustly handle user inputs using HTML `<form>` elements client side, and form
submission handlers server side.
---

For stronger resiliency and user experience, Fresh relies on native browser
support for form submissions with the HTML `<form>` element.

In the browser, a `<form>` submit will send an HTML action (usually `GET` or
`POST`) to the server, which responds with a new page to render.

## POST request with `application/x-www-form-urlencoded`

Forms typically submit as a `GET` request with data encoded in the URL's search
parameters, or as a `POST` request with either an
`application/x-www-form-urlencoded` or `multipart/form-data` body.

This example demonstrates how to handle `application/x-www-form-urlencoded`
`<form>` submissions:

```tsx routes/subscribe.tsx
import { define } from "../utils/state.ts";

export const handler = define.handler({
async POST(req, ctx) {
const form = await req.formData();
const email = form.get("email")?.toString();

// Add email to list.

// Redirect user to thank you page.
const headers = new Headers();
headers.set("location", "/thanks-for-subscribing");
return new Response(null, {
status: 303, // See Other
headers,
});
},
});

export default define.page<typeof handler>(function Subscribe() {
return (
<form method="post">
<input type="email" name="email" value="" />
<button type="submit">Subscribe</button>
</form>
);
});
```

When the user submits the form, Deno will retrieve the `email` value using the
request's `formData()` method, add the email to a list, and redirect the user to
a thank you page.

## Handling file uploads

File uploads can be handled in a very similar manner to the example above. Note
that this time, we have to explicitly declare the form's encoding to be
`multipart/form-data`.

```tsx routes/subscribe.tsx
import { page } from "fresh";

export const handler = {
async GET(req, ctx) {
return page({ data: { message: null } });
},
async POST(req, ctx) {
const form = await req.formData();
const file = form.get("my-file") as File;

if (!file) {
return page({
data: {
message: `Please try again`,
},
});
}

const name = file.name;
const contents = await file.text();

console.log(contents);

return page({
data: {
message: `${name} uploaded!`,
},
});
},
};

export default define.page<typeof handler>(function Upload(props) {
const { message } = props.data;
return (
<>
<form method="post" encType="multipart/form-data">
<input type="file" name="my-file" />
<button type="submit">Upload</button>
</form>
{message ? <p>{message}</p> : null}
</>
);
});
```

## A note of caution

These examples are simplified to demonstrate how Deno and Fresh handle HTTP
requests. In the Real World™, you'll want to validate your data (_especially the
file type_) and protect against cross-site request forgery. Consider yourself
warned.
Loading
Loading