diff --git a/package.json b/package.json
index a7171c21039..cac3ce74670 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "qwik-monorepo",
- "version": "0.0.20-2",
+ "version": "0.0.20-3",
"scripts": {
"build": "yarn node scripts --tsc --build --api --eslint --platform-binding --wasm",
"build.platform": "yarn node scripts --platform-binding",
diff --git a/packages/create-qwik/package.json b/packages/create-qwik/package.json
index 58d956127dc..11a23bf7114 100644
--- a/packages/create-qwik/package.json
+++ b/packages/create-qwik/package.json
@@ -1,6 +1,6 @@
{
"name": "create-qwik",
- "version": "0.0.20-2",
+ "version": "0.0.20-3",
"description": "Interactive CLI and API for generating Qwik projects.",
"bin": "create-qwik",
"main": "index.js",
diff --git a/packages/docs/functions/[[path]].ts b/packages/docs/functions/[[path]].ts
index d2e8f158ae8..52cf45427a7 100644
--- a/packages/docs/functions/[[path]].ts
+++ b/packages/docs/functions/[[path]].ts
@@ -40,7 +40,7 @@ export const onRequestGet: PagesFunction = async ({ request, next, waitUntil })
const opts: RenderToStringOptions = {
url: request.url,
base: '/build/',
- manifest,
+ manifest: manifest as any,
prefetchStrategy: {
symbolsToPrefetch: 'events-document',
implementation: 'link-prefetch',
diff --git a/packages/docs/pages/docs/README.md b/packages/docs/pages/docs/README.md
new file mode 100644
index 00000000000..9dd75862aed
--- /dev/null
+++ b/packages/docs/pages/docs/README.md
@@ -0,0 +1,36 @@
+# Guide
+
+## Guides
+
+- [Overview](overview.mdx)
+- [Getting Started](getting-started.mdx)
+- [Think Qwik](think-qwik.mdx)
+
+## Component API
+
+- [Overview](components/overview.mdx)
+- [Anatomy](components/anatomy.mdx)
+- [Hooks](components/hooks.mdx)
+- [Events](components/events.mdx)
+- [Lite elements](components/lite-components.mdx)
+- [Content projection](components/projection.mdx)
+- [Rendering](components/rendering.mdx)
+
+## Concepts
+
+- [Resumable](concepts/resumable.mdx)
+- [Progressive](concepts/progressive.mdx)
+- [Reactive](concepts/reactivity.mdx)
+
+## Advanced
+
+- [Containers](advanced/container.mdx)
+- [Optimizer](advanced/optimizer.mdx)
+- [QRL](advanced/qrl.mdx)
+- [Qwikloader](advanced/qwikloader.mdx)
+
+## Community
+
+- [Github](https://github.com/BuilderIO/qwik)
+- [@QwikDev](https://twitter.com/QwikDev)
+- [Discord](https://qwik.builder.io/chat)
diff --git a/packages/docs/pages/guide/containers/overview.mdx b/packages/docs/pages/docs/advanced/containers.mdx
similarity index 98%
rename from packages/docs/pages/guide/containers/overview.mdx
rename to packages/docs/pages/docs/advanced/containers.mdx
index 1cc3f82695b..f63140bfef5 100644
--- a/packages/docs/pages/guide/containers/overview.mdx
+++ b/packages/docs/pages/docs/advanced/containers.mdx
@@ -4,8 +4,6 @@ title: Containers
# Containers
-**\*NOTE**: Work in progress / Not fully implemented\*
-
Containers are a way to break up an application into smaller parts. We call these parts containers. Containers bring several benefits. Each container on the page can independently be:
- **resumed**: Each container can be resumed independently from all other components on the page. Independent resumability further reduces the amount of state which resume deserializes.
diff --git a/packages/docs/pages/docs/advanced/optimizer.mdx b/packages/docs/pages/docs/advanced/optimizer.mdx
new file mode 100644
index 00000000000..9cc7f04c417
--- /dev/null
+++ b/packages/docs/pages/docs/advanced/optimizer.mdx
@@ -0,0 +1,356 @@
+---
+title: Optimizer Rules
+fetch: https://hackmd.io/@mhevery/rkydmxJZ9
+---
+
+
+# Optimizer
+
+Qwik's philosophy is to delay loading code for as long as possible. To do that, Qwik relies on Optimizer to re-arrange the code for lazy loading. The Optimizer is code level transformation that runs as part of the rollup. (Optimizer is written in Rust (and available as WASM) for instant performance)
+
+The Optimizer looks for `$` and applies a transformation that extracts the expression following the `$` and turns it into a lazy-loadable and importable symbol.
+
+Let's start by looking at a simple `Counter` example:
+
+```tsx
+const Counter = component$(() => {
+ const store = useStore({ count: 0 });
+
+ return ;
+});
+```
+
+The above code represents what a developer would write to describe the component. Below are the transformations that the Optimizer applies to the code to make the code lazy-loadable.
+
+```tsx
+const Counter = component(qrl('./chunk-a.js', 'Counter_onMount'));
+```
+
+`chunk-a.js`:
+
+```tsx
+export const Counter_onMount = () => {
+ const store = useStore({ count: 0 });
+ return qrl('./chunk-b.js', 'Counter_onRender', [store]);
+};
+```
+
+`chunk-b.js`:
+
+```tsx
+const Counter_onRender = () => {
+ const [store] = useLexicalScope();
+ return (
+
+ );
+};
+```
+
+`chunk-c.js`:
+
+```tsx
+const Counter_onClick = () => {
+ const [store] = useLexicalScope();
+ return store.count++;
+};
+```
+
+
+# `$` and Optimizer Rules
+
+Optimizer runs as part of the bundling step of building the application. The purpose of the Optimizer is to break up the application into many small lazy-loadable chunks. The Optimizer moves expressions (usually functions) into new files and leaves behind a reference pointing to where the expression was moved.
+
+## The meaning of `$`
+
+The Optimizer needs to know which expression should be extracted into a new file. Extracting a symbol is complicated because the reference to the symbol changes from direct to asynchronous loading. This means that Optimizer needs to cooperate with the runtime to know which symbols can be extracted and how the runtime can then load them.
+
+Let's look at hypothetical problem of performing an action on scroll. You may be tempted to write the code like so:
+
+```tsx
+function onScroll(fn: () => void) {
+ document.addEventListener('scroll', fn);
+}
+
+onScroll(() => alert('scroll'));
+```
+
+The problem with this approach is that the event handler is eagerly loaded, even if the scroll event never triggers. What is needed is a way to refer to code in a lazy loadable way.
+
+The developer could write:
+
+```tsx
+export scrollHandler = () => alert('scroll');
+
+onScroll(() => (await import('./some-chunk')).scrollHandler());
+```
+
+This works but is a lot of work. The developer is responsible for putting the code in a different file and hard coding the chunk name. Instead, we use Optimizer to perform the work for us automatically. But we need a way to tell Optimizer that we want to perform such a refactoring. We use `$()` as a marker function for this purpose.
+
+```tsx
+function onScroll(fnQrl: QRL<() => void>) {
+ document.addEventListener('scroll', async () => {
+ fn = await qImport(document, fnQrl);
+ fn();
+ });
+}
+
+onScroll($(() => alert('clicked')));
+```
+
+The Optimizer will generate:
+
+```tsx
+onScroll(qrl('./chunk-a.js', 'onScroll_1'));
+```
+
+`chunk-a.js`:
+
+```tsx
+export const onScroll_1 = () => alert('scroll');
+```
+
+Notice:
+
+1. All that the developer had to do was to wrap the function in the `$()` to signal to the Optimizer that the function should be moved to a new file and therefore lazy-loaded.
+2. The `onScroll` had to be implemented slightly differently as it needs to take into account the fact that the `QRL` of the function needs to be loaded before it can be used. In practice using `qImport` is rare in Qwik application as the Qwik framework provide higher-level APIs that rarely expect the developer to work with `qImport` directly.
+
+However, wrapping code in `$()` is a bit inconvenient. For this reason, Optimizer implicitly wraps the first argument of any function call, which ends with `$`. (Additionally, one can use `implicit$FirstArg()` to automatically perform the wrapping and type matching of the function taking the `QRL`.)
+
+```tsx
+const onScroll$ = implicit$FirstArg(onScroll);
+
+onScroll$(() => alert('scroll'));
+```
+
+Now the developer has a very easy syntax for expressing that a particular function should be lazy-loaded.
+
+## Symbol extraction
+
+Assume that you have this code:
+
+```tsx
+const MyComp = component$(() => {
+ /* my component definition */
+});
+```
+
+The Optimizer breaks the code up into two files:
+
+The original file:
+
+```tsx
+const MyComp = component(qrl('./chunk-a.js', 'MyComp_onMount'));
+```
+
+`chunk-a.js`:
+
+```tsx
+export const MyComp_onMount = () => {
+ /* my component definition */
+});
+```
+
+The result of Optimizer is that the `MyComp`'s `onMount` method was extracted into a new file. There are a few benefits to doing this:
+
+- A Parent component can refer to `MyComp` without pulling in `MyComp` implementation details.
+- The application now has more entry points, giving the bundler more ways to chunk up the codebase.
+
+See also: Capturing Lexical Scope.
+
+# Capturing the lexical scope
+
+The Optimizer extracts expressions (usually functions) into new files and leaves behind a `QRL` pointing to the lazy-loaded location.
+
+Let's look at a simple case:
+
+```tsx
+const Greeter = component$(() => {
+ return Hello World!;
+});
+```
+
+this will result in:
+
+```tsx
+const Greeter = component(qrl('./chunk-a.js', 'Greeter_onMount'));
+```
+
+`chunk-a.js`:
+
+```tsx
+const Greeter_onMount = () => {
+ return qrl('./chunk-b.js', 'Greeter_onRender');
+};
+```
+
+`chunk-b.js`:
+
+```tsx
+const Greeter_onRender = () => Hello World!;
+```
+
+The above is for simple cases where the extracted function closure does not capture any variables. Let's look at a more complicated case where the extracted function closure lexically captures variables.
+
+```tsx
+const Greeter = component$((props: { name: string }) => {
+ const salutation = 'Hello';
+
+ return (
+
+ {salutation} {props.name}!
+
+ );
+});
+```
+
+The naive way to extract functions will not work.
+
+```tsx
+const Greeter = component(qrl('./chunk-a.js', 'Greeter_onMount'));
+```
+
+`chunk-a.js`:
+
+```tsx
+const Greeter_onMount = (props) => {
+ const salutation = 'Hello';
+ return qrl('./chunk-b.js', 'Greeter_onRender');
+};
+```
+
+`chunk-b.js`:
+
+```tsx
+const Greeter_onRender = () => (
+
+ {salutation} {props.name}!
+
+);
+```
+
+The issue can be seen in `chunk-b.js`. The extracted function refers to `salutation` and `props`, which are no longer in the lexical scope of the function. For this reason, the generated code must be slightly different.
+
+`chunk-a.js`:
+
+```tsx
+const Greeter_onMount = (props) => {
+ const salutation = 'Hello';
+ return qrl('./chunk-b.js', 'Greeter_onRender', [salutation, props]);
+};
+```
+
+`chunk-b.js`:
+
+```tsx
+const Greeter_onRender = () => {
+ const [salutation, props] = useLexicalScope();
+
+ return (
+
+ {salutation} {props.name}!
+
+ );
+};
+```
+
+Notice two changes:
+
+1. The `QRL` in `Greeter_onMount` now stores the `salutation` and `props`. This performs the role of capturing the constants inside closures.
+2. The generated closure `Greeter_onRender` now has a preamble which restores the `salutation` and `props` (`const [salutation, props] = useLexicalScope()`.)
+
+The ability for the Optimizer (and Qwik runtime) to capture lexically scoped constants significantly improves which functions can be extracted into lazy-loaded resources. It is a powerful tool for breaking up complex applications into smaller lazy-loadable chunks.
+
+# Optimizer Rules
+
+The Optimizer can break up large applications into lots of small lazy-loadable chunks. In addition, the Optimizer can lazy-load function closure, which lexically captures variables. However, there are limits to what can be achieved, and therefore the Optimizer comes with a set of rules. Not all valid Javascript is valid Optimizer code. This section describes the rules that developer needs to follow for successful Optimizer transformation.
+
+The `$` is not only a marker for the Optimizer but also a marker for the developer to follow these rules.
+
+NOTE: There are plans for a linter that will be able to enforce these rules eagerly.
+
+## Imports
+
+**RULE**: If a function that is being extracted by Optimizer refers to a top-level symbol, that symbol must either be imported or exported.
+
+```tsx
+import { importedFn } from '...';
+export exportedFn = () => {...};
+
+const salutation = "Hello";
+
+someApi$(() => {
+ importedFn(); // OK
+ exportedFn(); // OK
+ salutation; // Error: salutation not imported/exported
+})
+```
+
+The reason for the above rule becomes obvious when the output is examined.
+
+```tsx
+import { importedFn } from '...';
+export exportedFn = () => { ... };
+
+const salutation = "Hello";
+
+someApi(qrl('./chunk-a.js', 'someApi_1'));
+```
+
+`chunk-a.js`:
+
+```tsx
+import { importedFn } from '...';
+import { exportedFn } from './originalFile';
+
+export const someApi_1 = () => {
+ importedFn(); // OK
+ exportedFn(); // OK
+ salutation; // Error: no way to get reference to this.
+};
+```
+
+## Closures
+
+**RULE**: If a function lexically captures a variable (or parameter), that variable must be (1) a `const` and (2) the value must be serializable.
+
+```tsx
+function somefn() {
+ let count = 0;
+ list.foreach((item) => {
+ count++;
+ const currentCount = count;
+ someApi$(() => {
+ item; // OK (assuming serializable)
+ count; // ERROR: count not const
+ currentCount; // OK (assuming serializable)
+ });
+ });
+}
+```
+
+Again looking at the generated code reveals why these rules must be so:
+
+```tsx
+function somefn() {
+ let count = 0;
+ list.foreach((item) => {
+ count++;
+ const currentCount = count;
+ someApi$(qrl('./chunk-a.js', '_1', [item, count, currentCount]));
+ });
+}
+```
+
+`chunk-a.js`:
+
+```tsx
+export _1 = () => {
+ const [item, count, currentCount] = useLexicalScope();
+
+ item; // OK (assuming serializable)
+ count; // ERROR: count not const
+ currentCount; // OK (assuming serializable)
+};
+```
+
+See serialization for discussion of what is serializable.
diff --git a/packages/docs/pages/guide/components/qrl.mdx b/packages/docs/pages/docs/advanced/qrl.mdx
similarity index 100%
rename from packages/docs/pages/guide/components/qrl.mdx
rename to packages/docs/pages/docs/advanced/qrl.mdx
diff --git a/packages/docs/pages/guide/qwikloader.mdx b/packages/docs/pages/docs/advanced/qwikloader.mdx
similarity index 100%
rename from packages/docs/pages/guide/qwikloader.mdx
rename to packages/docs/pages/docs/advanced/qwikloader.mdx
diff --git a/packages/docs/pages/guide/components/host-element.mdx b/packages/docs/pages/docs/components/anatomy.mdx
similarity index 61%
rename from packages/docs/pages/guide/components/host-element.mdx
rename to packages/docs/pages/docs/components/anatomy.mdx
index d4a0a8ce893..271ac5598d2 100644
--- a/packages/docs/pages/guide/components/host-element.mdx
+++ b/packages/docs/pages/docs/components/anatomy.mdx
@@ -3,33 +3,78 @@ title: Host element
fetch: https://hackmd.io/@mhevery/ryD4BhAg9
---
-# Host Element
+# Component anatomy
-The host element is an element in the DOM that represents component boundaries.
+A component is a small, reusable piece of code that can be used to build a UI.
-Lite-components do not have host elements.
+In Qwik, they are declared using the `component$` method:
```tsx
-const Child = () => child;
+import { component$, useStore } from '@builder.io/qwik';
-const Parent = () => (
-
-
-
-);
+// Qwik components can be asynchronous
+export const MyCmp = component$(async (props: MyCmpProps) => {
+ // Declare local state
+ const state = useStore({
+ count: 0,
+ });
+
+ // Returns JSX
+ return (
+
+ Hello, {props.name} {state.count}
+
Times: {state.count}
+
+
+ );
+});
```
-Will result in:
+## Props
-```html
-
- child
-
+Props are used to pass data into the a component. Props are declared as named arguments of the component.
+
+In this example a component `Item` declares optional `name`, `quantity`, `description`, and `price`.
+
+```tsx
+interface ItemProps {
+ name?: string,
+ quantity?: number,
+ description?: string,
+ price?: number
+}
+
+export const Item = component$((props: ItemProps) => {
+ return ...;
+});
```
-Looking at the resulting HTML, it is not possible to tell if a single component or multiple components produced the HTML. It is also not possible to discern where the component boundaries are.
+The resulting component can be used like so:
-On the other hand, Qwik-components rely on host elements because it must be possible by looking on the HTML to determine where one component starts and another ends. Without knowing the boundaries, it would not be possible to render components independently without forcing parent/child components to render as well. This is a crucial feature of Qwik.
+```tsx
+const MyApp = component$(() => {
+ return (
+ <>
+ - With no props:
+ - With some props:
+ - With all props:
+ >
+ );
+});
+```
+
+## Host element
+
+The host element is an element in the DOM that represents component boundaries.
+
+Qwik-components rely on host elements because it must be possible by looking on the HTML to determine where one component starts and another ends. Without knowing the boundaries, it would not be possible to render components independently without forcing parent/child components to render as well. This is a crucial feature of Qwik.
```tsx
const Child = component$(() => child);
@@ -53,72 +98,38 @@ Will result in:
```
-Qwik host elements are marked with `q:host` attribute. (Default element is `div` for host element, but that can be changed with `component$` options argument.) Qwik uses host elements in various ways. For example when using `useHostElement()` function which retrieves it. It is also used to attach props to the components for serialization.
-
-## Lazy Loading
-
-The host component also serves an important role when breaking parent-child relationships for bundling purposes.
-
-```tsx
-const Child = () => child;
-
-const Parent = () => (
-
-
-
-);
-```
+Qwik host elements are marked with `q:host` attribute.
-In the above example, referring to the `Parent` component implies a transitive reference to the `Child` component. When bundler is creating chunk, a reference to `Parent` necessitates bundling `Child` as well. (`Parent` internally refers to `Child`.) These transitive dependencies are a problem because it means that having a reference to the root component will transitively refer to the remainder of the application—something which Qwik tries to avoid explicitly.
+As you can see, `component$` will always create an extra element, by default is `div`, but that can be changed with `component$` options argument:
```tsx
-const Child = component$(() => {
- return child;
-});
-
-const Parent = component$(() => {
- return (
-
-
-
- );
+const MyArticle = component$(() => (
+ My article
+), {
+ tagName: 'article'
});
```
-In the above example the Optimizer transforms the above to:
+Will result in:
-```tsx
-const Child = component$(qrl('./chunk-a', 'Child_onMount'));
-const Parent = component$(qrl('./chunk-b', 'Parent_onMount'));
-const Parent_onMount = () => qrl('./chunk-c', 'Parent_onRender');
-const Parent_onRender = () => (
-
-
-
-);
+```html
+
+
+ My article
+
+
```
-NOTE: for simplicity, not all of the transformations are shown; all resulting symbols are kept in the same file for succinctness.
+### `useHostElement()`
-Notice that after the optimizer transforms the code, the `Parent` no longer directly references `Child`. This is important because it allows the bundler (and tree shakers) to freely move the symbols into different chunks without pulling the rest of the application with it.
-
-So what happens when the `Parent` component renders and `Child` component has not yet been downloaded? First, the `Parent` component renders its JSX like so.
+Since the host element is implicitally created by `component$`, it is not possible to access it directly. Instead, you can use `useHostElement()` to get the host element.
-```html
-
-
-
-
-
-```
+ Qwik uses host elements in various ways. For example when using `useHostElement()` function which retrieves it. It is also used to attach props to the components for serialization.
-As you can see in the above example, the `` acts as a marker where the `Child` component will be inserted once it is lazy-loaded.
-## Mental Model
-The optimizer splits Qwik components into the host element and the behavior of the component. The host element gets bundled with the parent components OnRender function, whereas the component's behavior is something that gets lazy-loaded on an as-needed basis.
-## Host ELement Attributes & Styling
+## Host Element Attributes & Styling
Assume you have a component defined as so:
@@ -151,7 +162,13 @@ Because the host element is an actual element, there may be a desire to place HT
What if you wanted to add a `name` attribute to the host element? The issue is that the `name` is already used by the component props. For this reason, we use `host:` prefix to refer to the host element's attributes.
```tsx
- {}} name="world" />
+ {}}
+
+ name="world"
+/>
```
would render as:
@@ -174,11 +191,15 @@ would render:
```
-However, many IDEs will not recognize `host:class` and `host:style` and would not trigger code completion. For this reason, `class` and `styles` are special, and they will automatically map to `host:class` and `host:styles`
+However, for convenience `class` and `styles` are special, and they will automatically map to `host:class` and `host:styles`
when placed on the host element.
```jsx
-
+
```
would also render to the some output.
@@ -208,3 +229,68 @@ will result in:
Hello World
```
+
+
+## Lazy Loading
+
+The host component also serves an important role when breaking parent-child relationships for bundling purposes.
+
+```tsx
+const Child = () => child;
+
+const Parent = () => (
+
+
+
+);
+```
+
+In the above example, referring to the `Parent` component implies a transitive reference to the `Child` component. When bundler is creating chunk, a reference to `Parent` necessitates bundling `Child` as well. (`Parent` internally refers to `Child`.) These transitive dependencies are a problem because it means that having a reference to the root component will transitively refer to the remainder of the application—something which Qwik tries to avoid explicitly.
+
+
+```tsx
+const Child = component$(() => {
+ return child;
+});
+
+const Parent = component$(() => {
+ return (
+
+
+
+ );
+});
+```
+
+In the above example the Optimizer transforms the above to:
+
+```tsx
+const Child = component$(qrl('./chunk-a', 'Child_onMount'));
+const Parent = component$(qrl('./chunk-b', 'Parent_onMount'));
+const Parent_onMount = () => qrl('./chunk-c', 'Parent_onRender');
+const Parent_onRender = () => (
+
+
+
+);
+```
+
+NOTE: for simplicity, not all of the transformations are shown; all resulting symbols are kept in the same file for succinctness.
+
+Notice that after the optimizer transforms the code, the `Parent` no longer directly references `Child`. This is important because it allows the bundler (and tree shakers) to freely move the symbols into different chunks without pulling the rest of the application with it.
+
+So what happens when the `Parent` component renders and `Child` component has not yet been downloaded? First, the `Parent` component renders its JSX like so.
+
+```html
+
+
+
+
+
+```
+
+As you can see in the above example, the `` acts as a marker where the `Child` component will be inserted once it is lazy-loaded.
+
+## Mental Model
+
+The optimizer splits Qwik components into the host element and the behavior of the component. The host element gets bundled with the parent components OnRender function, whereas the component's behavior is something that gets lazy-loaded on an as-needed basis.
diff --git a/packages/docs/pages/docs/components/events.mdx b/packages/docs/pages/docs/components/events.mdx
new file mode 100644
index 00000000000..8a4ccc53920
--- /dev/null
+++ b/packages/docs/pages/docs/components/events.mdx
@@ -0,0 +1,255 @@
+---
+title: Events
+---
+
+# Events
+
+For a web application to be interactive, there needs to be a way to respond to user events. This is done by registering callback functions in the JSX template.
+
+```tsx
+const Counter = component$(() => {
+ const store = useStore({ count: 0 });
+
+ return ;
+});
+```
+
+In the above example, the `onClick$` attribute of the `