Skip to content

Commit

Permalink
docs: Add 'No-Build Workflows' doc, focusing on importmaps (for now) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
rschristian authored Aug 13, 2024
1 parent 551d77b commit d87bef4
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 49 deletions.
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].

[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
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

0 comments on commit d87bef4

Please sign in to comment.