diff --git a/website/documents/guides/07-lifecycles.md b/website/documents/guides/07-lifecycles.md
index 849c93be..970060c0 100644
--- a/website/documents/guides/07-lifecycles.md
+++ b/website/documents/guides/07-lifecycles.md
@@ -165,6 +165,60 @@ function *Component() {
renderer.render(, document.body);
```
+## Cleanup logic
+While you can use context iterators to write cleanup logic after `for...of` and `for await...of` loops, this does not handle errors, and this cleanup logic cannot be written outside of component functions. To solve the first issue, you can use `try`/`finally`. When a generator component is removed from the tree, Crank calls the `return` method on the component’s generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced by a `return` statement. This means any loops your component was in when the generator suspended are broken out of, and code after the yield does not execute.
+
+You can take advantage of this behavior by wrapping your `yield` loops in a `try`/`finally` block to release any resources that your component may have used.
+
+```jsx
+import {renderer} from "@b9g/crank/dom";
+
+function *Cleanup() {
+ try {
+ while (true) {
+ yield "Hi";
+ }
+ } finally {
+ console.log("finally block executed");
+ }
+}
+
+renderer.render(, document.body);
+console.log(document.body); // "Hi"
+renderer.render(null, document.body);
+// "finally block executed"
+console.log(document.body); // ""
+```
+
+[The same best practices](https://eslint.org/docs/rules/no-unsafe-finally) which apply to `try`/`finally` statements in regular functions apply to generator components. In short, you should not yield or return anything in the `finally` block. Crank will not use the yielded or returned values and doing so might cause your components to inadvertently swallow errors or suspend in unexpected locations.
+
+To write cleanup logic which can be abstractd outside the component function, you can use the `cleanup()` method on the context. This method is similar to `flush() and `schedule()` in that it takes a callback.
+
+
+```jsx live
+import {renderer} from "@b9g/crank/dom";
+function addGlobalEventListener(ctx, type, listener, options) {
+ window.addEventListener(type, listener, options);
+ // ctx.cleanup allows you to write cleanup logic outside the component
+ ctx.cleanup(() => window.removeEventListener(type, listener, options));
+}
+
+function *KeyboardListener() {
+ let key = "";
+ const listener = (ev) => {
+ key = ev.key;
+ this.refresh();
+ };
+
+ addGlobalEventListener(this, "keypress", listener);
+ for ({} of this) {
+ yield
Last key pressed: {key || "N/A"}
+ }
+}
+
+renderer.render(, document.body);
+```
+
## Catching Errors
It can be useful to catch errors thrown by components to show the user an error notification or to notify error-logging services. To facilitate this, Crank will cause `yield` expressions to rethrow errors which happen when rendering children. You can take advantage of this behavior by wrapping your `yield` operations in a `try`/`catch` block to catch errors caused by children.
@@ -202,32 +256,28 @@ function *Catcher() {
renderer.render(, document.body);
```
-## Additional cleanup methods
-
-When a generator component is removed from the tree, Crank calls the `return` method on the component’s generator object. You can think of it as whatever `yield` expression your component was suspended on being replaced by a `return` statement. This means any loops your component was in when the generator suspended are broken out of, and code after the yield does not execute.
+## Returning values from generator components
-You can take advantage of this behavior by wrapping your `yield` loops in a `try`/`finally` block to release any resources that your component may have used.
+When you return from a generator component, the returned value is rendered and the component scope is thrown away, same as would happen when using a function component. This means that while the component cannot have local variables, but represent sequences of renderings.
-```jsx
+```jsx live
import {renderer} from "@b9g/crank/dom";
+function *Component() {
+ yield
+ );
}
}
-renderer.render(, document.body);
-console.log(document.body); // "Hi"
-renderer.render(null, document.body);
-// "finally block executed"
-console.log(document.body); // ""
+renderer.render(, document.body);
```
-
-[The same best practices](https://eslint.org/docs/rules/no-unsafe-finally) which apply to `try`/`finally` statements in regular functions apply to generator components. In short, you should not yield or return anything in the `finally` block. Crank will not use the yielded or returned values and doing so might cause your components to inadvertently swallow errors or suspend in unexpected locations.
-
-## Returning Values
diff --git a/website/documents/guides/08-reusable-logic.md b/website/documents/guides/08-reusable-logic.md
index 14d9c56a..04c57843 100644
--- a/website/documents/guides/08-reusable-logic.md
+++ b/website/documents/guides/08-reusable-logic.md
@@ -55,10 +55,13 @@ Anything can be passed as a key to the `provide` and `consume` methods, so you c
**Note:** Crank does not link “providers” and “consumers” in any way, and doesn’t automatically refresh consumer components when the `provide` method is called. It’s up to you to ensure consumers update when providers update.
-### context.schedule
-You can pass a callback to the `schedule` method to listen for when the component renders. Callbacks passed to `schedule` fire synchronously after the component commits, with the rendered value of the component as its only argument. Scheduled callbacks fire once per call and callback function (think `requestAnimationFrame`, not `setInterval`). This means you have to continuously call the `schedule` method for each update if you want to execute some code every time your component commits.
+### `context.schedule()`
+You can pass a callback to the `schedule()` method to listen for when the component renders. Callbacks passed to `schedule` fire synchronously after the component renders, with the rendered value of the component as its only argument. Scheduled callbacks fire once per call and callback function per update (think `requestAnimationFrame()`, not `setInterval()`). This means you have to continuously call the `schedule` method for each update if you want to execute some code every time your component commits.
-### context.cleanup
+### `context.flush()`
+Similar to the `schedule()` method, this method fires when rendering has finished. Unlike the `schedule()` method, this method fires when a component’s rendered DOM is live. This is useful when you need to do something like calling `focus()` on an `` element or perform DOM measurement calculations. Callbacks fire once per call and callback function per update.
+
+### `context.cleanup()`
Similarly, you can pass a callback to the `cleanup` method to listen for when the component unmounts. Callbacks passed to `cleanup` fire synchronously when the component is unmounted. Each registered callback fires only once. The callback is called with the last rendered value of the component as its only argument.
## Strategies for Reusing Logic
@@ -73,7 +76,7 @@ import {Context} from "@bikeshaving/crank";
const ContextIntervalSymbol = Symbol.for("ContextIntervalSymbol");
-Context.prototype.setInterval = function(callback, delay, ...args) {
+Context.prototype.setInterval = function(callback, delay, ...args) {`
const interval = window.setInterval(callback, delay, ...args);
if (typeof this[ContextIntervalSymbol] === "undefined") {
this[ContextIntervalSymbol] = new Set();
diff --git a/website/src/views/guide.ts b/website/src/views/guide.ts
index 2c95613a..834e40d0 100644
--- a/website/src/views/guide.ts
+++ b/website/src/views/guide.ts
@@ -36,7 +36,6 @@ export default async function Guide({
<${Sidebar} docs=${docs} url=${url} title="Guides" />
<${Main}>