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: Add 'No-Build Workflows' doc, focusing on importmaps (for now) #1175

Merged
merged 5 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 8 additions & 49 deletions content/en/guide/v10/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Writing raw `h` or `createElement` calls can be tedious. JSX has the advantage o
>
> `import { html, render } from 'https://esm.sh/htm/preact/standalone'`

For a more full example, see [Using Preact with HTM and ImportMaps](#using-preact-with-htm-and-importmaps), and for more information on HTM, check out its [documentation][htm].
For a more scalable solution, see [Import Maps -- Basic Usage](/guide/v10/no-build-workflows#basic-usage), and for more information on HTM, check out its [documentation][htm].
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to refer to import maps as more scalable? Obviously HTTP imports have "issues", but I'm not sure of the language here.


[htm]: https://github.com/developit/htm

Expand Down Expand Up @@ -252,61 +252,20 @@ our best to fix these), and as such, these libraries could be the source of Type
errors. By setting `skipLibCheck`, you can tell TS that it doesn't need to do a full check of all
`.d.ts` files (usually these are limited to your libraries in `node_modules`) which will fix these errors.

### Using Preact with HTM and ImportMaps

An [Import Map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) is a newer feature
that allows you to control how browsers resolve module specifiers, usually to convert bare specifiers such as `preact`
to a CDN URL like `https://esm.sh/preact`. While many do prefer the aesthetics import maps can provide, there are also
real advantages to using them, such as more control over module resolution (read on to see how to alias) and solving
the burden (as well as possible bugs) that comes with copying CDN URLs from file to file.

Here's an example of a import map in use:
#### Aliasing with Import Maps

```html
<script type="importmap">
{
"imports": {
"preact": "https://esm.sh/[email protected]",
"preact/": "https://esm.sh/[email protected]/",
"htm/preact": "https://esm.sh/[email protected]/preact?external=preact"
"preact": "https://esm.sh/[email protected]",
"preact/": "https://esm.sh/[email protected]/",
"react": "https://esm.sh/[email protected]/compat",
"react/": "https://esm.sh/[email protected]/compat/",
"react-dom": "https://esm.sh/[email protected]/compat",
}
}
</script>

<script type="module">
import { render } from 'preact';
import { useReducer } from 'preact/hooks';
import { html } from 'htm/preact';

export function App() {
const [count, add] = useReducer((a, b) => a + b, 0);

return html`
<button onClick=${() => add(-1)}>Decrement</button>
<input readonly size="4" value=${count} />
<button onClick=${() => add(1)}>Increment</button>
`;
}

render(html`<${App} />`, document.body);
</script>
```

> **Note:** We use `?external=preact` in the example above as many CDNs will helpfully provide the
> module you're asking for as well as its dependencies. However, this can trip up Preact as it (and
> React too) expect to be loaded as singletons (only 1 instance active at a time). Using `?external`
> tells `esm.sh` that it doesn't need to provide a copy of `preact`, we can handle that ourselves with
> our import map

You can even use import maps to support aliasing:

```html
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/[email protected]/compat",
"react-dom": "https://esm.sh/[email protected]/compat"
}
}
</script>
```
See also [Import Maps -- Recipes and Common Patterns](/guide/v10/no-build-workflows#recipes-and-common-patterns) for more examples.
123 changes: 123 additions & 0 deletions content/en/guide/v10/no-build-workflows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
name: No-Build Workflows
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched this over to 'No-Build Workflows' instead, as an importmap-specific page felt unnecessary. Granted, this might be a bit confusing for Deno users, but I felt that the ability to more info here in the future for buildless workflows is of more benefit.

Will think on it, anyhow. Opinions certainly welcome (as always).

description: 'Whilst build tools like Webpack, Rollup, and Vite are incredibly powerful and useful, Preact fully supports building applications without them.'
---

# No-Build Workflows

Whilst build tools like Webpack, Rollup, and Vite are incredibly powerful and useful, Preact fully supports building
applications without them.

No-build workflows are a way to develop web applications while forgoing build tooling, instead relying on the browser
to facilitate module loading and execution. This is a great way to get started wtih Preact and can continue to work
very well at all scales, but isn't entirely without difficulties.

---

<div><toc></toc></div>

---

## Import Maps

An [Import Map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) is a newer feature
that allows you to control how browsers resolve module specifiers, often to convert bare specifiers such as `preact`
to a CDN URL like `https://esm.sh/preact`. While many do prefer the aesthetics import maps can provide, there are also
objective advantages to the centralization of dependencies, such as easier versioning, reduced/removed duplication, and
better access to more powerful CDN features.

This isn't to say you need import maps, but for those choosing to forgo build tooling, they are a great option to at least
be aware of.

### Basic Usage

[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) has a great deal of information on how to
utilize import maps, but a basic example looks like the following:

```html
<!DOCTYPE html>
<html>
<head>
<script type="importmap">
{
"imports": {
"preact": "https://esm.sh/[email protected]",
"htm/preact": "https://esm.sh/[email protected]/preact?external=preact"
}
}
</script>
</head>
<body>
<div id="app"></div>

<script type="module">
import { render } from 'preact';
import { html } from 'htm/preact';

export function App() {
return html`
<h1>Hello, World!</h1>
`;
}

render(html`<${App} />`, document.getElementById('app'));
</script>
</body>
</html>
```

We create a `<script>` tag with a `type="importmap"` attribute, and then define the modules we'd like to use
inside of it as JSON. Later, in a `<script type="module">` tag, we can import these modules using bare specifiers,
similiar to what you'd see in Node.

> **Note:** We use `?external=preact` in the example above as https://esm.sh will helpfully provide the
> module you're asking for as well as its dependencies -- for `htm/preact`, this means also providing a
> copy of `preact`. However, Preact and many other libraries need to be used as singletons (only a single
> active instance at a time) which creates a problem.
>
> By using `?external=preact`, we tell `esm.sh` that it shouldn't provide a copy of `preact`, we can handle
> that ourselves. Therefore, the browser will use our importmap to resolve `preact`, using the same Preact
> instance as the rest of our code.

### Recipes and Common Patterns

While not an exhaustive list, here are some common patterns and recipes you may find useful when working with
import maps. If you have a pattern you'd like to see, [let us know](https://github.com/preactjs/preact-www/issues/new)!

For these examples we'll be using https://esm.sh as our CDN -- it's a brilliant, ESM-focused CDN that's a bit
more flexible and powerful than some others, but by no means are you limited to it. However you choose to serve
your modules, make sure you're familiar with the policy regarding dependencies: duplication of `preact` and some
other libraries will cause (often subtle and unexpected) issues. For `esm.sh`, we address this with the `?external`
query parameter, but other CDNs may work differently.

#### Preact with Hooks, Signals, and HTM

```html
<script type="importmap">
{
"imports": {
"preact": "https://esm.sh/[email protected]",
"preact/": "https://esm.sh/[email protected]/",
"@preact/signals": "https://esm.sh/@preact/[email protected]?external=preact",
"htm/preact": "https://esm.sh/[email protected]/preact?external=preact"
}
}
</script>
```

#### Aliasing React to Preact

```html
<script type="importmap">
{
"imports": {
"preact": "https://esm.sh/[email protected]",
"preact/": "https://esm.sh/[email protected]/",
"react": "https://esm.sh/[email protected]/compat",
"react/": "https://esm.sh/[email protected]/compat/",
"react-dom": "https://esm.sh/[email protected]/compat",
"@mui/material": "https://esm.sh/@mui/[email protected]?external=react,react-dom"
}
}
</script>
```
6 changes: 6 additions & 0 deletions src/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,12 @@
{
"path": "/typescript",
"name": "TypeScript"
},
{
"path": "/no-build-workflows",
"name": {
"en": "No-Build Workflows"
}
}
]
}
Expand Down