diff --git a/.eslintignore b/.eslintignore
index dced7b27f9c..e376423dea9 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -17,4 +17,5 @@ tsdoc-metadata.json
scripts/**/*
*.tsbuildinfo
packages/docs/src/pages/examples/**/*
+packages/docs/src/pages/tutorial/**/*
vite.config.ts
diff --git a/cspell.json b/cspell.json
index 2c3933cf3fd..840b5bd78ab 100644
--- a/cspell.json
+++ b/cspell.json
@@ -34,5 +34,5 @@
"Pinterest",
"Serializability"
],
- "enableFiletypes": ["!mdx"]
+ "enableFiletypes": ["mdx"]
}
diff --git a/packages/docs/src/pages/docs/INDEX b/packages/docs/src/pages/docs/INDEX
index 0ec811689b3..95924c9acdb 100644
--- a/packages/docs/src/pages/docs/INDEX
+++ b/packages/docs/src/pages/docs/INDEX
@@ -19,6 +19,11 @@
- [Content projection](components/projection.mdx)
- [Rendering](components/rendering.mdx)
+## Cheat Sheet
+
+- [Components](cheat/components.mdx)
+- [Serialization](cheat/serialization.mdx)
+
## Concepts
- [Resumable](concepts/resumable.mdx)
diff --git a/packages/docs/src/pages/docs/advanced/containers.mdx b/packages/docs/src/pages/docs/advanced/containers.mdx
index f63140bfef5..62327d36ec3 100644
--- a/packages/docs/src/pages/docs/advanced/containers.mdx
+++ b/packages/docs/src/pages/docs/advanced/containers.mdx
@@ -40,7 +40,7 @@ A typical site is composed of two logical parts:
1. The navigation that tends to stay constant across many pages, and
2. The outlet, which is the part of the page that changes based on which route the user navigated to.
-We can model the two parts as two navigation and outlet containers. When the user first navigates to a route, the server responds with HTML, which contains containers for both the navigation and the outlet. Once the user navigates to the second route, there are three ways to solve the navigation:
+We can model the two parts as two navigation and outlet containers. When the user first navigates to a route, the server responds with HTML, that contains containers for both the navigation and the outlet. Once the user navigates to the second route, there are three ways to solve the navigation:
1. The simplistic approach is to make a full round trip and download an entirely new page. The main downside is that the application loses all of its states on the client.
1. The classical approach is to treat any further navigation in JavaScript. We replace the current outlet component with the new outlet component and let the new component render. The disadvantage is that we need to download and execute the JavaScript.
diff --git a/packages/docs/src/pages/docs/advanced/optimizer.mdx b/packages/docs/src/pages/docs/advanced/optimizer.mdx
index 9ce839287b0..88d67f3e33d 100644
--- a/packages/docs/src/pages/docs/advanced/optimizer.mdx
+++ b/packages/docs/src/pages/docs/advanced/optimizer.mdx
@@ -31,22 +31,15 @@ const Counter = component(qrl('./chunk-a.js', 'Counter_onMount'));
```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 (
- {store.count}
+
+ {store.count}
+
);
};
```
-`chunk-c.js`:
+`chunk-b.js`:
```tsx
const Counter_onClick = () => {
@@ -55,6 +48,8 @@ const Counter_onClick = () => {
};
```
+Notice that every occurence of `$` results in a new lazy loadable symbol.
+
# `$` and Optimizer Rules
diff --git a/packages/docs/src/pages/docs/cheat/components.mdx b/packages/docs/src/pages/docs/cheat/components.mdx
new file mode 100644
index 00000000000..204a602e151
--- /dev/null
+++ b/packages/docs/src/pages/docs/cheat/components.mdx
@@ -0,0 +1,98 @@
+---
+title: Components
+---
+
+# Common Component Patterns Cheat Sheet
+
+## Declartion
+
+```tsx
+import {component$, useStore} from "@builder.io/qwik";
+
+export const Greeter = component$(() => {
+ return Hello World! ;
+});
+```
+
+## Props
+
+```tsx
+import {component$, useStore} from "@builder.io/qwik";
+
+interface GreeterProps {
+ salutation?: string;
+ name?: string;
+}
+export const Greeter = component$((props: GreeterProps) => {
+ const salutation = props.salutation || 'Hello';
+ const name = props.name || 'World';
+ return {salutation} {name}! ;
+});
+```
+
+### Event Props
+
+Component props must be serializable, and therefore can not directly reffer to functions.
+
+```tsx
+import {component$, useStore, Qrl} from "@builder.io/qwik";
+
+export const Parent = component$(() => {
+ return (
+ console.log('Hello')}>
+ click
+
+ );
+});
+
+interface MyButtonProps {
+ doSomethingQrl: QRL<() => void>
+}
+export const MyButton = component$((props: MyButtonProps) => {
+ return click ;
+});
+```
+
+## Events
+
+## Watching for Changes
+
+## Server
+### Fetching Data
+
+```tsx
+import {component$, useStore, useServerMount$} from "@builder.io/qwik";
+
+export const Greeter = component$(() => {
+ const store = useStore<{list: null|string[]}>({list: null});
+ useServerMount$(async () => {
+ store.list = await doSomethingToFetchDataOnServer();
+ });
+
+ return (
+
+ {store.list && store.list.map((item) => {item} )}
+
+ );
+});
+```
+
+## Client
+### Eagerly Executing Code
+
+```tsx
+import {component$, useStore, useClientEffet} from "@builder.io/qwik";
+
+export const Greeter = component$(() => {
+ const store = useStore<{list: null|string[]}>({list: null});
+ useClientEffet$(async () => {
+ store.list = await doSomethingToFetchDataOnServer();
+ });
+
+ return (
+
+ {store.list && store.list.map((item) => {item} )}
+
+ );
+});
+```
diff --git a/packages/docs/src/pages/docs/cheat/serialization.mdx b/packages/docs/src/pages/docs/cheat/serialization.mdx
new file mode 100644
index 00000000000..8bbc2528614
--- /dev/null
+++ b/packages/docs/src/pages/docs/cheat/serialization.mdx
@@ -0,0 +1,53 @@
+---
+title: Serialization & Serialization Boundaries
+---
+
+# `$` Boundaries
+
+## Rules
+
+- Only serializable data can cross a `$` boundery.
+
+## Serialization Boundery
+
+A serialization boundery occures whenever you cross a lexical scope of a function that is converted into lazy loadable form. It is always denoted by `$(...)` (or `____$(...)`) See example:
+
+```tsx
+import {component$} from "@builder.io/qwik";
+
+export const topLevel = Promise.resolve('nonserializable data');
+
+export const Greeter = component$(() => {
+ // BEGIN component serialization boundery
+
+ // Referring to top level symbols that are exported is always allowed.
+ console.log(topLevel); // OK
+
+ const captureSerializable = 'serializable data';
+ const captureNonSerializable = Promise.resolve('nonserializable data');
+ return (
+ {
+ // BEGIN onClick serialization boundery
+
+ // Referring to top level symbols that are exported is always allowed.
+ // Even if the value is non-serializable.
+ console.log(topLevel); // OK
+
+ // Capturing a non-toplevel variable is allowed only if:
+ // - declaed as `const`
+ // - is serializable (runtime error)
+ console.log(captureSerializable); // OK
+
+ // Reffering to captureNonSerializable will pass static analysis but
+ // will fail at runtime because Qwik does not know how to serilize it.
+ console.log(captureNonSerializable); // RUNTIME ERROR
+
+ // END onClick serialization boundery
+ }}>
+ click
+
+ );
+ // BEGIN component serialization boundery
+});
+
+```
\ No newline at end of file
diff --git a/packages/docs/src/pages/docs/components/anatomy.mdx b/packages/docs/src/pages/docs/components/anatomy.mdx
index eb41a2a972b..07a32180b87 100644
--- a/packages/docs/src/pages/docs/components/anatomy.mdx
+++ b/packages/docs/src/pages/docs/components/anatomy.mdx
@@ -265,8 +265,8 @@ const Parent = component$(() => {
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 Child = componentQrl(qrl('./chunk-a', 'Child_onMount'));
+const Parent = componentQrl(qrl('./chunk-b', 'Parent_onMount'));
const Parent_onMount = () => qrl('./chunk-c', 'Parent_onRender');
const Parent_onRender = () => (
diff --git a/packages/docs/src/pages/docs/components/events.mdx b/packages/docs/src/pages/docs/components/events.mdx
index d8b9edff26e..2db7de65ec7 100644
--- a/packages/docs/src/pages/docs/components/events.mdx
+++ b/packages/docs/src/pages/docs/components/events.mdx
@@ -67,7 +67,7 @@ The main point here is that while the syntax of the events is consistent between
## Prevent default
-Because of the async nature of Qwik, event's handler execution might be delayed because the implementation is not downloaded yet. This introduces a problem when the event's handler needs to prevent the default behavior of the event. Tradicional `event.preventDefault()` will not work, instead use the qwik's `preventdefault:{eventName}` attribute:
+Because of the async nature of Qwik, event's handler execution might be delayed because the implementation is not downloaded yet. This introduces a problem when the event's handler needs to prevent the default behavior of the event. Traditional `event.preventDefault()` will not work, instead use the qwik's `preventdefault:{eventName}` attribute:
```tsx
const Counter = component$(() => {
@@ -269,7 +269,7 @@ Notice that `on:click` attribute contains three pieces of information:
2. `Counter_button_onClick`: The symbol which needs to be retrieved from the lazy-loaded chunk.
3. `[0]`: An array of lexically captured variable references (State of the closure).
-In our case `() => store.count++` only captures `store`, and hence it contains only a single reference `0`. `0` is an index into the `q:obj` attribute which contains a reference to the actual serialized object referring to `store`. (The exact mechanisms and syntax is an implementation detail that can change at any time.)
+In our case `() => store.count++` only captures `store`, and hence it contains only a single reference `0`. `0` is an index into the `q:obj` attribute that contains a reference to the actual serialized object referring to `store`. (The exact mechanisms and syntax is an implementation detail that can change at any time.)
## Comparison to `import()`
@@ -277,7 +277,7 @@ JavaScript supports dynamic `import()`. At first glance, it may seem that the sa
Dynamic `import()`:
-- Is relative to the file which contains it. This works great for `file-a.js` trying to load `file-b.js` as `import('./file-b.js')`. However, when the `./file-a.js` gets serialized into HTML then we lose its relative nature. It is the framework that reads the `./file-b.js` from HTML and performs the `import()`. This means that all imports now become relative to the framework, which is incorrect.
+- Is relative to the file that contains it. This works great for `file-a.js` trying to load `file-b.js` as `import('./file-b.js')`. However, when the `./file-a.js` gets serialized into HTML then we lose its relative nature. It is the framework that reads the `./file-b.js` from HTML and performs the `import()`. This means that all imports now become relative to the framework, which is incorrect.
- Requires that the developer writes `import('./file-a.js')`, which means the developer is in charge of deciding where the lazy-loaded boundaries are. This limits our ability of the tooling to move code around in an automated way.
- Supports import of top-level functions only which don't capture the state. This is the biggest difference. Qwik allows the imported symbol to be a closure that carries all of its state with it.
diff --git a/packages/docs/src/pages/docs/getting-started.mdx b/packages/docs/src/pages/docs/getting-started.mdx
index dbb1ad3acc2..04df0320d4e 100644
--- a/packages/docs/src/pages/docs/getting-started.mdx
+++ b/packages/docs/src/pages/docs/getting-started.mdx
@@ -50,7 +50,7 @@ After your new app is created, you will see an output like the following in your
npm start
```
-At this point, you will have `qwik-todo` directory, which contains the starter app.
+At this point, you will have `qwik-todo` directory, that contains the starter app.
## Running in development
diff --git a/packages/docs/src/pages/docs/overview.mdx b/packages/docs/src/pages/docs/overview.mdx
index cb7448c282a..fd9f9155527 100644
--- a/packages/docs/src/pages/docs/overview.mdx
+++ b/packages/docs/src/pages/docs/overview.mdx
@@ -5,16 +5,16 @@ fetch: https://hackmd.io/@mhevery/Sy52N2Ax9
# Overview
-Qwik is a new kind of web framework that can deliver instant loading web applications at any size or complexity. Your sites and apps can boot with less than 1kb of JS (_including_ your code, regardless of complexity), and achieve unheard of performance at scale.
+Qwik is a new kind of web framework that can deliver instantly load web applications at any size or complexity. Your sites and apps can boot with about 1kb of JS (regardless of application complexity), and achieve consistent performance at scale.
## Qwik is:
- **General-purpose**: Qwik is familiar for React developers and can be used to build any type of web site or application.
-- **Resumable**: Qwik is [resumable](./concepts/resumable.mdx) which means Qwik applications require **0 hydration**. This allows Qwik apps to have instant-on interactivity.
+- **Resumable**: Qwik is [resumable](./concepts/resumable.mdx) which means Qwik applications require **no hydration**. This allows Qwik apps to have instant-on interactivity.
- **Progressive**: Qwik takes [full responsibility of how to load and download JS](https://www.builder.io/blog/dont-blame-the-developer-for-what-the-frameworks-did). No more manual code splitting.
- **Reactive**: Qwik semantics allow for [fully reactive and efficient rendering](./concepts/reactivity.mdx).
-- **Fast**: Qwik has unprecedented performance, offering sub-second full page loads even on mobile devices. Qwik achieves this by delivering pure HTML, and incrementally loading JS only as-needed.
-- **Scalable**: Qwik application have [O(1) constant scalability](https://www.builder.io/blog/our-current-frameworks-are-on-we-need-o1). It does not matter if your application has 1 million components, boot time is unaffected.
+- **Fast**: Qwik has consistantly fast performance no matter the application complexity, offering sub-second full page loads even on mobile devices. Qwik achieves this by delivering pure HTML, and incrementally loading JS only as-needed.
+- **Scalable**: Qwik application have [O(1) constant scalability](https://www.builder.io/blog/our-current-frameworks-are-on-we-need-o1). It does not matter the size of your application, boot time is constantly fast.
{
+ return __put_something_here__ ;
+});
diff --git a/packages/docs/src/pages/tutorial/component/basic/solution/app.tsx b/packages/docs/src/pages/tutorial/component/basic/solution/app.tsx
new file mode 100644
index 00000000000..7dca358de75
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/basic/solution/app.tsx
@@ -0,0 +1,5 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return Hello World! ;
+});
diff --git a/packages/docs/src/pages/tutorial/component/binding/index.mdx b/packages/docs/src/pages/tutorial/component/binding/index.mdx
new file mode 100644
index 00000000000..9c6a3400fea
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/binding/index.mdx
@@ -0,0 +1,9 @@
+---
+title: Binding Expressions
+layout: tutorial
+---
+
+The purpose of components is to merge data with the template (JSX.) This is done with the help of the `{expression}` syntax and can be placed either as a text node or attribute.
+
+- Bind `data.name` to the `value` attribute of ` `.
+- Bind `data.description` to the content of ``.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/component/binding/problem/app.tsx b/packages/docs/src/pages/tutorial/component/binding/problem/app.tsx
new file mode 100644
index 00000000000..e3ca99ef14e
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/binding/problem/app.tsx
@@ -0,0 +1,26 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ // @ts-ignore
+ const data = {
+ name: 'Qwik',
+ description: DESCRIPTION,
+ };
+
+ return (
+ <>
+
+
+
+ >
+ );
+});
+
+export const DESCRIPTION = `
+Qwik is designed for the fastest possible page load time,
+by delivering pure HTML with near 0 JavaScript for your
+pages to become interactive, regardless of how complex
+your site or app is. It achieves this via resumability
+of code.`;
diff --git a/packages/docs/src/pages/tutorial/component/binding/solution/app.tsx b/packages/docs/src/pages/tutorial/component/binding/solution/app.tsx
new file mode 100644
index 00000000000..a426889039c
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/binding/solution/app.tsx
@@ -0,0 +1,25 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const data = {
+ name: 'Qwik',
+ description: DESCRIPTION,
+ };
+
+ return (
+ <>
+
+
+
+ >
+ );
+});
+
+export const DESCRIPTION = `
+Qwik is designed for the fastest possible page load time,
+by delivering pure HTML with near 0 JavaScript for your
+pages to become interactive, regardless of how complex
+your site or app is. It achieves this via resumability
+of code.`;
diff --git a/packages/docs/src/pages/tutorial/component/composition/index.mdx b/packages/docs/src/pages/tutorial/component/composition/index.mdx
new file mode 100644
index 00000000000..babaaa4d2a3
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/composition/index.mdx
@@ -0,0 +1,8 @@
+---
+title: Component Composition
+layout: tutorial
+---
+
+Components can be composed together to build applications.
+
+In this example we have pre-declared an `` and a `` component. Place the `` component inside the `` component so that the user can see its contents.
diff --git a/packages/docs/src/pages/tutorial/component/composition/problem/app.tsx b/packages/docs/src/pages/tutorial/component/composition/problem/app.tsx
new file mode 100644
index 00000000000..c763fd4884e
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/composition/problem/app.tsx
@@ -0,0 +1,14 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+
+ Insert Greeter component here. By composing components together large applications can be
+ written without putting all of the code into a single file/component.
+
+ );
+});
+
+export const Greeter = component$(() => {
+ return Hello World!
;
+});
diff --git a/packages/docs/src/pages/tutorial/component/composition/solution/app.tsx b/packages/docs/src/pages/tutorial/component/composition/solution/app.tsx
new file mode 100644
index 00000000000..47c6d7e7cb7
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/composition/solution/app.tsx
@@ -0,0 +1,13 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+
+
+
+ );
+});
+
+export const Greeter = component$(() => {
+ return Hello World!
;
+});
diff --git a/packages/docs/src/pages/tutorial/component/inline/index.mdx b/packages/docs/src/pages/tutorial/component/inline/index.mdx
new file mode 100644
index 00000000000..fd77934ab3b
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/inline/index.mdx
@@ -0,0 +1,10 @@
+---
+title: Light Component
+layout: tutorial
+---
+
+Qwik comports are lazy-loadable. This is achieved through `component$()` method. The presence of `$` crates a lazy-loaded boundary. The `$` will be discussed later. Qwik also supports inline components. Inline components are not by themselves lazy loadable and instead load as part of the parent component where they are used. (Inline components are equivalent to regular components in most other frameworks.)
+
+In this example we have pre-declared an `` and a `` component. The ` ` component is currently a Qwik Component because it is declared with `component$()`. Remove the `component$()` to convert `` to inline component. Inline components do not have host elements, (described later.)
+
+Open the `Symbols` tab and notice that the ` ` component is no longer an independent export, and instead it is bundled as part of the `` component.
diff --git a/packages/docs/src/pages/tutorial/component/inline/problem/app.tsx b/packages/docs/src/pages/tutorial/component/inline/problem/app.tsx
new file mode 100644
index 00000000000..47c6d7e7cb7
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/inline/problem/app.tsx
@@ -0,0 +1,13 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+
+
+
+ );
+});
+
+export const Greeter = component$(() => {
+ return Hello World!
;
+});
diff --git a/packages/docs/src/pages/tutorial/component/inline/solution/app.tsx b/packages/docs/src/pages/tutorial/component/inline/solution/app.tsx
new file mode 100644
index 00000000000..c577ffd6750
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/component/inline/solution/app.tsx
@@ -0,0 +1,13 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+
+
+
+ );
+});
+
+export const Greeter = () => {
+ return Hello World!
;
+};
diff --git a/packages/docs/src/pages/tutorial/composing/dollar/index.mdx b/packages/docs/src/pages/tutorial/composing/dollar/index.mdx
new file mode 100644
index 00000000000..502b9cee5dd
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/composing/dollar/index.mdx
@@ -0,0 +1,68 @@
+---
+title: Creating APIs with $
+layout: tutorial
+---
+
+The powerful, part of Optimizer is that you can create your own APIs with `$` suffix.
+
+Imagine that we would like to have a delay method that lazy loads its callback. Normally we would have to write something like this:
+
+```typescript
+setTimeout(() => {
+ // I am eagerly loaded, but it would be better if I was lazy-loaded.
+ ...
+}, timeout);
+```
+
+The issue with the example above is that the callback has to be downloaded and created eagerly. This may be an issue if the closure is large or if the callback is never executed (or only executed later.)
+
+A better solution would be to have `delay$` method that can lazy-load the closure associated with the callback. Something like this.
+
+```typescript
+delay$(() => {
+ // I am lazy-loaded only when I need to be executed.
+ ...
+}, 1000)
+```
+
+In the above solution, the callback is only downloaded when `delay$` is ready to execute it.
+
+## Creating your APIs with `$` suffix
+
+Qwik runtime works with `QRL`s. For this reason we define a method like so:
+```typescript
+export function delayQrl(fn: QRL<() => T>, delayInMs: number): Promise {
+ return new Promise((res) => {
+ setTimeout(() => {
+ res(fn.invoke());
+ }, delayInMs);
+ });
+}
+```
+
+This method knows how to take a `QRL` and execute it after a certain delay. The key part here is that the `QRL.invoke()` method is called when the delay is ready and is therefore lazy.
+
+The next step is to convert the `delayQrl` method to a `delay$` alias. This is done with `implicit$FirstArg` like so:
+
+```typescript
+export const delay$ = implicit$FirstArg(delayQrl);
+```
+
+Here are the types to make it clearer as to what is going on.
+
+```typescript
+declare delayQrl: (fn: QRL<() => T>, delayInMs: number) => Promise;
+declare delay$: (fn: () => T, delayInMs: number) => Promise;
+```
+
+The above allows us to use `delay$` in an inlined fashion, but the Optimizer converts the `delay$` to `delayQrl` form.
+
+NOTE the two methods must have the same prefix. So a general form is:
+```typescript
+export const SOME_NAME_Qrl = ...;
+export const SOME_NAME_$ = implicit$FirstArg(SOME_NAME_Qrl);
+```
+
+## Example
+
+In our example we are executing `store.count++` and `store.delay++` together. Wrap the `store.delay` in `delay$()` call so that it updates with a one second delay.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/composing/dollar/problem/app.tsx b/packages/docs/src/pages/tutorial/composing/dollar/problem/app.tsx
new file mode 100644
index 00000000000..d2603129116
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/composing/dollar/problem/app.tsx
@@ -0,0 +1,30 @@
+import { component$, QRL, implicit$FirstArg, useStore } from '@builder.io/qwik';
+
+export function delayQrl(fn: QRL<() => T>, delayInMs: number): Promise {
+ return new Promise((res) => {
+ setTimeout(() => {
+ res(fn.invoke());
+ }, delayInMs);
+ });
+}
+
+export const delay$ = implicit$FirstArg(delayQrl);
+
+export const App = component$(() => {
+ const store = useStore({ count: 0, delay: 0 });
+ return (
+ <>
+ Count: {store.count}
+ Delay: {store.delay}
+ {
+ store.count++;
+ // This code should be delayed by 1000ms
+ store.delay++;
+ }}
+ >
+ +1
+
+ >
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/composing/dollar/solution/app.tsx b/packages/docs/src/pages/tutorial/composing/dollar/solution/app.tsx
new file mode 100644
index 00000000000..2fd9ccf8407
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/composing/dollar/solution/app.tsx
@@ -0,0 +1,29 @@
+import { component$, QRL, implicit$FirstArg, useStore } from '@builder.io/qwik';
+
+export function delayQrl(fn: QRL<() => T>, delayInMs: number): Promise {
+ return new Promise((res) => {
+ setTimeout(() => {
+ res(fn.invoke());
+ }, delayInMs);
+ });
+}
+
+export const delay$ = implicit$FirstArg(delayQrl);
+
+export const App = component$(() => {
+ const store = useStore({ count: 0, delay: 0 });
+ return (
+ <>
+ Count: {store.count}
+ Delay: {store.delay}
+ {
+ store.count++;
+ await delay$(() => store.delay++, 1000);
+ }}
+ >
+ +1
+
+ >
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/composing/use/index.mdx b/packages/docs/src/pages/tutorial/composing/use/index.mdx
new file mode 100644
index 00000000000..08bbde895e9
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/composing/use/index.mdx
@@ -0,0 +1,11 @@
+---
+title: Composing use Hooks
+layout: tutorial
+---
+
+Hooks are a way to abstract common logic away from the components that use it. They are a way to share logic between components. While Qwik provides many hooks, there will always be one that is not provided out of the box. This tutorial will show you how to create your own hook.
+
+In this example, the registering of `mousemove` events is something that could be shared between multiple components. Refactor the code by pulling out the code before JSX into its own `useMousePosition()` function.
+
+Congratulations, you have successfully created your own hook! You can now use it in any component that needs to listen to the mouse position.
+
diff --git a/packages/docs/src/pages/tutorial/composing/use/problem/app.tsx b/packages/docs/src/pages/tutorial/composing/use/problem/app.tsx
new file mode 100644
index 00000000000..f3c65847ec5
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/composing/use/problem/app.tsx
@@ -0,0 +1,17 @@
+import { component$, useOnDocument, useStore, $ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const mousePosition = useStore({ x: 0, y: 0 });
+ useOnDocument(
+ 'mousemove',
+ $((event: Event) => {
+ mousePosition.x = (event as MouseEvent).clientX;
+ mousePosition.y = (event as MouseEvent).clientY;
+ })
+ );
+ return (
+
+ (x: {mousePosition.x}, y: {mousePosition.y})
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/composing/use/solution/app.tsx b/packages/docs/src/pages/tutorial/composing/use/solution/app.tsx
new file mode 100644
index 00000000000..a772374c3db
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/composing/use/solution/app.tsx
@@ -0,0 +1,22 @@
+import { component$, useOnDocument, useStore, $ } from '@builder.io/qwik';
+
+export function useMousePosition() {
+ const mousePosition = useStore({ x: 0, y: 0 });
+ useOnDocument(
+ 'mousemove',
+ $((event: Event) => {
+ mousePosition.x = (event as MouseEvent).clientX;
+ mousePosition.y = (event as MouseEvent).clientY;
+ })
+ );
+ return mousePosition;
+}
+
+export const App = component$(() => {
+ const mousePosition = useMousePosition();
+ return (
+
+ (x: {mousePosition.x}, y: {mousePosition.y})
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/context/basic/index.mdx b/packages/docs/src/pages/tutorial/context/basic/index.mdx
new file mode 100644
index 00000000000..1a7695d14be
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/context/basic/index.mdx
@@ -0,0 +1,15 @@
+---
+title: Context
+layout: tutorial
+---
+
+Use context to pass data to child components without explicitly passing it through components (known as prop drilling). Context is useful to share data that is needed throughout the application components. For example styling information, application state, or currently logged-in user.
+
+To use context you need three parts:
+- `createContext()`: this creates a serializable ID for the context. Make sure that this id is unique within your application.
+- `useContextProvider()`: At a parent component call this method to publish the context value. All children (and grandchildren) that are descendants of this component will be able to retrieve the context.
+- `useContext()` to retrieve the context and use it in any component.
+
+In this example, we would like to pass the `TodosStore` to the `` component. Update the code to use `useContext()` to retrieve the value.
+
+Contexts typically are stores, and as such, they must be serializable.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/context/basic/problem/app.tsx b/packages/docs/src/pages/tutorial/context/basic/problem/app.tsx
new file mode 100644
index 00000000000..b3a1593063e
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/context/basic/problem/app.tsx
@@ -0,0 +1,28 @@
+import { component$, createContext, useContextProvider, useStore } from '@builder.io/qwik';
+
+interface TodosStore {
+ items: string[];
+}
+export const TodosContext = createContext('Todos');
+export const App = component$(() => {
+ useContextProvider(
+ TodosContext,
+ useStore({
+ items: ['Learn QWik', 'Build Qwik app', 'Profit'],
+ })
+ );
+
+ return ;
+});
+
+export const Items = component$(() => {
+ // replace this with context retrieval.
+ const todos = { items: [] };
+ return (
+
+ {todos.items.map((item) => (
+ {item}
+ ))}
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/context/basic/solution/app.tsx b/packages/docs/src/pages/tutorial/context/basic/solution/app.tsx
new file mode 100644
index 00000000000..b6125fba79e
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/context/basic/solution/app.tsx
@@ -0,0 +1,33 @@
+import {
+ component$,
+ createContext,
+ useContextProvider,
+ useContext,
+ useStore,
+} from '@builder.io/qwik';
+
+interface TodosStore {
+ items: string[];
+}
+export const TodosContext = createContext('Todos');
+export const App = component$(() => {
+ useContextProvider(
+ TodosContext,
+ useStore({
+ items: ['Learn Qwik', 'Build Qwik app', 'Profit'],
+ })
+ );
+
+ return ;
+});
+
+export const Items = component$(() => {
+ const todos = useContext(TodosContext);
+ return (
+
+ {todos.items.map((item) => (
+ {item}
+ ))}
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/events/basic/index.mdx b/packages/docs/src/pages/tutorial/events/basic/index.mdx
new file mode 100644
index 00000000000..b899484c08d
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/basic/index.mdx
@@ -0,0 +1,45 @@
+---
+title: Event Listeners
+layout: tutorial
+---
+
+So far we have created static applications that have no interactivity. An important reason why we use frameworks to build applications is for easy declaration of behavior. A key part of application behavior is to listen to user events.
+
+There are many different events browser support that Qwik can listen on. The general syntax is to place an attribute `on$` on an element to signal to the framework that we wish to listen to events here.
+
+Add a `click` event on the `` by adding the `onClick$` property and bind it to a function that will `alert('Hello World!')`.
+
+## Understanding the Runtime
+
+So far this may look like a normal framework behavior. However, Qwik does things very differently from current frameworks and it is worth understanding what is happening under the hood.
+
+Every time you see `$` you should be thinking lazy-loading happens here.
+
+If you open dev-tools you will see that no JavaScript is loaded until you click the button. This is achieved through several steps described below.
+
+To better understand what is going on we have included simplified HTML of the application here. Click on the HTML tab to see actual HTML produced by the server.
+```HTML
+
+
+
+ Click Me
+
+
+
+
+```
+
+
+1. The APIs contain `$` to ensure that your code contains them.
+2. The Optimizer looks for `$` and extracts the function wrapped by `$` into a separate lazy-loadable chunk. (See the documentation to better understand how this is done.)
+3. As part of the SSR the server executes the JSX and notices that there is a click listener. The click listener gets serialized into the `` element as `on:click` attribute. (The unique behavior is that the Qwik knows how to subscribe to events as part of the SSR.)
+4. Qwikloader scripts get inlined into HTML. The Qwikloader script sets up a global listener for all events in the browser. (Qwikloader is about 1kb and executes in about 1ms.)
+5. When a user clicks on the button the Qwikloader intercepts the event and looks for an element with `on:click` attribute.
+6. The `on:click` attribute contains:
+ - URL that contains the code to be downloaded (Qwik also set up prefetching of code so there is no delay between click and execution.)
+ - A symbol that is exported by the downloaded chunk that contains the function to execute.
+7. Once the code is downloaded the function is executed.
+
+The above behavior is what gives Qwik applications their instant-on property (resumability) without eagerly downloading and executing code and performing hydration which is expensive.
+
+NOTE: To ensure that there is no delay between user action and response, Qwik supports prefetching of code.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/events/basic/problem/app.tsx b/packages/docs/src/pages/tutorial/events/basic/problem/app.tsx
new file mode 100644
index 00000000000..28a432ccd15
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/basic/problem/app.tsx
@@ -0,0 +1,5 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return Click Me ;
+});
diff --git a/packages/docs/src/pages/tutorial/events/basic/solution/app.tsx b/packages/docs/src/pages/tutorial/events/basic/solution/app.tsx
new file mode 100644
index 00000000000..c22978e0593
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/basic/solution/app.tsx
@@ -0,0 +1,5 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return alert('Hello World!')}>Click Me ;
+});
diff --git a/packages/docs/src/pages/tutorial/events/document/index.mdx b/packages/docs/src/pages/tutorial/events/document/index.mdx
new file mode 100644
index 00000000000..66055f133a5
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/document/index.mdx
@@ -0,0 +1,14 @@
+---
+title: Listening to document/window
+layout: tutorial
+---
+
+So far we have shown you how to listen to events emitted from a DOM element such as a ``. But what if you need to listen for an event on `window` or `document` (for example a `mousemove` or `scroll` event.)
+
+Qwik has `document:` and `window:` namespace prefixes which allow you to listen to global events.
+
+Open the `Diagnostics` tab and notice that moving the mouse over text produces console output. However, this only works while moving over the text. That is because when the mouse is moved over an area other than text the event does not bubble through the listener.
+
+To fix this change the listener to be a global listener by adding `document:` prefix.
+
+Now notice that moving the mouse anywhere on the application (even outside of the text) will show output in the console.
diff --git a/packages/docs/src/pages/tutorial/events/document/problem/app.tsx b/packages/docs/src/pages/tutorial/events/document/problem/app.tsx
new file mode 100644
index 00000000000..041fad9d6e5
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/document/problem/app.tsx
@@ -0,0 +1,10 @@
+/* eslint-disable no-console */
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+ console.log(`x=${event.x}; y=${event.y}`)}>
+ Move your mouse over this text.
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/events/document/solution/app.tsx b/packages/docs/src/pages/tutorial/events/document/solution/app.tsx
new file mode 100644
index 00000000000..84f72f68475
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/document/solution/app.tsx
@@ -0,0 +1,10 @@
+/* eslint-disable no-console */
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+ console.log(`x=${event.x}; y=${event.y}`)}>
+ Move your mouse over this text.
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/events/preventdefault/index.mdx b/packages/docs/src/pages/tutorial/events/preventdefault/index.mdx
new file mode 100644
index 00000000000..4521fbb8439
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/preventdefault/index.mdx
@@ -0,0 +1,14 @@
+---
+title: Event Prevent Default
+layout: tutorial
+---
+
+For some events browsers have default behavior. For example, when you click a link, the browser will usually navigate to the link's `href`. There are times when we would like to prevent that. For this reason `Event` API contains `preventDefault()` method.
+
+Browser events are synchronous, but because Qwik is fine-grained loadable Qwik execution model is asynchronous. This means that at the time when the event is triggered, the event handler is not yet loaded. By the time the event is loaded the event has already been processed by the browser and calling `preventDefault()` will have no effect.
+
+To solve this Qwik provides a declarative API to automatically call `preventDefault()` when the event is triggered. This is achieved by adding `preventdefault:` attribute to the element. This allows the Qwikloader to synchronously call `preventDefault()` when the event is triggered.
+
+## Example
+
+Clicking on the link will cause a navigation event. We wish to prevent this and call our callback instead. Add `preventdeault:click` to the `` to achieve this.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/events/preventdefault/problem/app.tsx b/packages/docs/src/pages/tutorial/events/preventdefault/problem/app.tsx
new file mode 100644
index 00000000000..a681a3ba6d4
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/preventdefault/problem/app.tsx
@@ -0,0 +1,9 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+ alert('do something else.')}>
+ click me!
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/events/preventdefault/solution/app.tsx b/packages/docs/src/pages/tutorial/events/preventdefault/solution/app.tsx
new file mode 100644
index 00000000000..51466458f14
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/preventdefault/solution/app.tsx
@@ -0,0 +1,9 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+ alert('do something else.')}>
+ click me!
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/events/programmatic/index.mdx b/packages/docs/src/pages/tutorial/events/programmatic/index.mdx
new file mode 100644
index 00000000000..940decf22ae
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/programmatic/index.mdx
@@ -0,0 +1,19 @@
+---
+title: Programmatic Listeners
+layout: tutorial
+---
+
+There are times when a listener needs to be added conditionally, the listener name is not known, or a use method needs to set up a listener. Qwik provides a programmatic way of setting up listeners.
+
+Qwik provides these functions:
+- `useOn()` Add event on specific event on current host element.
+- `useOnDocument()` Add event on specific event on document.
+- `useOnWindow()`: Add event on specific event on window.
+- `useVisible$()`: Invoked when component becomes visible.
+- `useResume$()`: Invoked on eagerly on application loading.
+
+Set up a click listener on the component which will `alert('Hello world!')`.
+
+## Understanding the `$`
+
+`$` means lazy load this reference. When we are setting up a listener with `useOn` the second argument to `useOn` is a `QRL`. `QRL`s are lazy loadable references to your code. (More on QRLs later.) If `useOn` would take function directly (rather than QRL) it would have to be eagerly executed so that the JavaScript can allocate the listener closure. By converting it to QRL (using the `$` function) we can lazy load the closer only when `click` listener is triggered.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/events/programmatic/problem/app.tsx b/packages/docs/src/pages/tutorial/events/programmatic/problem/app.tsx
new file mode 100644
index 00000000000..1ad4485dbbf
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/programmatic/problem/app.tsx
@@ -0,0 +1,7 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ // hint: useOn('click', $(() => ...));
+
+ return App Component. Click me.
;
+});
diff --git a/packages/docs/src/pages/tutorial/events/programmatic/solution/app.tsx b/packages/docs/src/pages/tutorial/events/programmatic/solution/app.tsx
new file mode 100644
index 00000000000..fe0e3c73ad4
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/programmatic/solution/app.tsx
@@ -0,0 +1,10 @@
+import { component$, useOn, $ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ useOn(
+ 'click',
+ $(() => alert('Hello World!'))
+ );
+
+ return App Component. Click me.
;
+});
diff --git a/packages/docs/src/pages/tutorial/events/synchronous/index.mdx b/packages/docs/src/pages/tutorial/events/synchronous/index.mdx
new file mode 100644
index 00000000000..6debed74d59
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/synchronous/index.mdx
@@ -0,0 +1,12 @@
+---
+title: Synchronous Event Processing
+layout: tutorial
+---
+
+At times it is necessary to process events synchronously. (This is not a common use case, so we are including it just in case.) This creates a problem for Qwik because all event processing in Qwik is asynchronous. This example shows how to eagerly load an event handler that can then process the event synchronously.
+
+Fortunately, this pattern is rare, therefore it is not something we need to do often.
+
+## Example
+
+Convert the `onClick$` from asynchronous event to synchronous event by using `useClientEffect` and normal event registration.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/events/synchronous/problem/app.tsx b/packages/docs/src/pages/tutorial/events/synchronous/problem/app.tsx
new file mode 100644
index 00000000000..fa7cdcf4c36
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/synchronous/problem/app.tsx
@@ -0,0 +1,9 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+ window.open('http://qwik.builder.io')}>
+ click me!
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/events/synchronous/solution/app.tsx b/packages/docs/src/pages/tutorial/events/synchronous/solution/app.tsx
new file mode 100644
index 00000000000..6b3e2ac8d76
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/events/synchronous/solution/app.tsx
@@ -0,0 +1,19 @@
+import { component$, useClientEffect$, useRef } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const aHref = useRef();
+ useClientEffect$(() => {
+ const handler = (event: Event) => {
+ event.preventDefault();
+ window.open('http://qwik.builder.io');
+ };
+ aHref.current!.addEventListener('click', handler);
+ return () => aHref.current!.removeEventListener('click', handler);
+ });
+
+ return (
+
+ click me!
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/hooks/cleanup/index.mdx b/packages/docs/src/pages/tutorial/hooks/cleanup/index.mdx
new file mode 100644
index 00000000000..46c59fe7db9
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/cleanup/index.mdx
@@ -0,0 +1,14 @@
+---
+title: useCleanup$() Hook
+layout: tutorial
+---
+
+Use `useCleanup$()` hook to execute code after the component is unmounted. A typical use case is to clean up resources such as timers or subscriptions. (Keep in mind that many methods such as `useWatch$()` allow returning a cleanup function, which may be a more idiomatic way to solve the problem.)
+
+## Component Life Cycle and SSR
+
+Qwik is resumable. Resumability means that the application starts up on the server and then the application is transferred to the client. On the client, the application continues execution from where it left off. The implication of this is that a component may be created on the server and destroyed on the client. This means that the component's `useMount$()` method may execute on the server and but its `useCleanu$()` method may execute on the client.
+
+## Example
+
+Modify the `` component to use `useCleanup$()` to alert the user when the component is unmounted.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/hooks/cleanup/problem/app.tsx b/packages/docs/src/pages/tutorial/hooks/cleanup/problem/app.tsx
new file mode 100644
index 00000000000..34e9551f05f
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/cleanup/problem/app.tsx
@@ -0,0 +1,16 @@
+import { component$, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ show: true });
+ return (
+
+ (store.show = !store.show)}>Toggle
+ {store.show ? : null}
+
+ );
+});
+
+export const Greeter = component$(() => {
+ // Use useCleanup$ here to alert the user when the component is removed.
+ return Hello World ;
+});
diff --git a/packages/docs/src/pages/tutorial/hooks/cleanup/solution/app.tsx b/packages/docs/src/pages/tutorial/hooks/cleanup/solution/app.tsx
new file mode 100644
index 00000000000..bf82b9eccb1
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/cleanup/solution/app.tsx
@@ -0,0 +1,18 @@
+import { component$, useCleanup$, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ show: true });
+ return (
+
+ (store.show = !store.show)}>Toggle
+ {store.show ? : null}
+
+ );
+});
+
+export const Greeter = component$(() => {
+ useCleanup$(() => {
+ alert('Greeter component has been removed!');
+ });
+ return Hello World ;
+});
diff --git a/packages/docs/src/pages/tutorial/hooks/client-effect/index.mdx b/packages/docs/src/pages/tutorial/hooks/client-effect/index.mdx
new file mode 100644
index 00000000000..a95a8533011
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/client-effect/index.mdx
@@ -0,0 +1,26 @@
+---
+title: useClientEffect$()
+layout: tutorial
+---
+
+Use `useClientEffect$()` to execute code after the component is resumed. This is useful for things such as setting up timers or streams on the client when the application is resumed.
+
+## Component Life Cycle and SSR
+
+Qwik is resumable. Resumability means that the application starts up on the server and then the application is transferred to the client. On the client, the application continues execution from where it left off. A common use case of this is that a component is created on the server, paused, and then resumed on the client. To make the component fully functional it may be necessary to execute code eagerly on the client to set up timers or streams.
+
+`useClientEffect$()` is a client only method. (There is no equivalent on the server.) NOTE: See `useWatch$()` for behavior that needs to be executed on both client and server.
+
+## When is `useClientEffect$()` executed?
+
+Client effect code is executed after the component is resumed. The `useClientEffect$()` method takes an additional argument that controls when the effect is executed. There are two options:
+- `visible` (default): Execute the effect when the component becomes visible. This is a preferred option because it delays the execution of the effect until the component is visible rather than eagerly on application startup (We are trying to minimize the amount of code the application runs on startup).
+- `load`: Execute the code as soon as possible. This is usually right after `DOMContentLoaded` event.
+
+## Example
+
+The example shows a clock component that is rendered below the fold. Use the `useClientEffect$()` to make the clock update the current time every second to make it work on the client. We have provided the utility function `updateClock` to aid your implementation.
+
+Keep in mind that `useClientEffect$()` should return a cleanup function that releases the `setInterval` timer so that the timer can be properly cleaned up when the component is unmounted/destroyed.
+
+
diff --git a/packages/docs/src/pages/tutorial/hooks/client-effect/problem/app.tsx b/packages/docs/src/pages/tutorial/hooks/client-effect/problem/app.tsx
new file mode 100644
index 00000000000..7deee910a03
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/client-effect/problem/app.tsx
@@ -0,0 +1,54 @@
+import { component$, useStore, useStyles$, useClientEffect$ } from '@builder.io/qwik';
+import styles from './clock.css';
+
+interface ClockStore {
+ hour: number;
+ minute: number;
+ second: number;
+}
+export const Clock = component$(() => {
+ useStyles$(styles);
+
+ const store = useStore({
+ hour: 0,
+ minute: 0,
+ second: 0,
+ });
+
+ useClientEffect$(() => {
+ // Put code here to periodically call updateClock().
+ });
+
+ return (
+
+ );
+});
+
+export function updateClock(store: ClockStore) {
+ const now = new Date();
+ store.second = now.getSeconds() * (360 / 60);
+ store.minute = now.getMinutes() * (360 / 60);
+ store.hour = now.getHours() * (360 / 12);
+}
+
+export const App = component$(() => {
+ return (
+
+
This is an example of Lazy executing code on component when component becomes visible.
+
+
+ ⬇️ Scroll down until the clock is in view.
+
+
+
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/hooks/client-effect/problem/clock.css b/packages/docs/src/pages/tutorial/hooks/client-effect/problem/clock.css
new file mode 100644
index 00000000000..d1c742f7e38
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/client-effect/problem/clock.css
@@ -0,0 +1,92 @@
+/* Clock inspired by: https://paulund.co.uk/create-a-clock-in-css */
+
+.clock {
+ background: #fff;
+ border: 10px solid #7a7a7a;
+ border-radius: 50%;
+ box-sizing: border-box;
+ height: 300px;
+ margin: 0 auto;
+ position: relative;
+ width: 300px;
+}
+
+.twelve,
+.three,
+.six,
+.nine {
+ background: #333;
+ position: absolute;
+}
+
+.twelve,
+.six {
+ height: 10px;
+ width: 4px;
+}
+
+.three,
+.nine {
+ height: 4px;
+ width: 10px;
+}
+
+.twelve {
+ left: 50%;
+ top: -1px;
+}
+
+.three {
+ right: -1px;
+ top: 50%;
+}
+
+.six {
+ left: 50%;
+ bottom: -1px;
+}
+
+.nine {
+ left: -1px;
+ top: 50%;
+}
+
+.hour {
+ height: 60px;
+ width: 4px;
+ background: #333;
+ position: absolute;
+ left: 50%;
+ top: 80px;
+ animation: tick 43200s infinite linear;
+ -webkit-animation: tick 43200s infinite linear;
+}
+
+.minute {
+ height: 100px;
+ width: 4px;
+ background: #777;
+ position: absolute;
+ left: 50%;
+ top: 40px;
+ animation: tick 3600s infinite linear;
+ -webkit-animation: tick 3600s infinite linear;
+}
+
+.second {
+ height: 120px;
+ width: 3px;
+ background: #fc0505;
+ position: absolute;
+ left: 50%;
+ top: 20px;
+ animation: tick 60s infinite linear;
+ -webkit-animation: tick 60s infinite linear;
+}
+
+.hour,
+.minute,
+.second {
+ transform-origin: 2px 100%;
+ -webkit-transform-origin: 2px 100%;
+}
diff --git a/packages/docs/src/pages/tutorial/hooks/client-effect/solution/app.tsx b/packages/docs/src/pages/tutorial/hooks/client-effect/solution/app.tsx
new file mode 100644
index 00000000000..0c266f81ab3
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/client-effect/solution/app.tsx
@@ -0,0 +1,56 @@
+import { component$, useStore, useStyles$, useClientEffect$ } from '@builder.io/qwik';
+import styles from './clock.css';
+
+interface ClockStore {
+ hour: number;
+ minute: number;
+ second: number;
+}
+export const Clock = component$(() => {
+ useStyles$(styles);
+
+ const store = useStore({
+ hour: 0,
+ minute: 0,
+ second: 0,
+ });
+
+ useClientEffect$(() => {
+ updateClock(store);
+ const tmrId = setInterval(() => updateClock(store), 1000);
+ return () => clearInterval(tmrId);
+ });
+
+ return (
+
+ );
+});
+
+export function updateClock(store: ClockStore) {
+ const now = new Date();
+ store.second = now.getSeconds() * (360 / 60);
+ store.minute = now.getMinutes() * (360 / 60);
+ store.hour = now.getHours() * (360 / 12);
+}
+
+export const App = component$(() => {
+ return (
+
+
This is an example of Lazy executing code on component when component becomes visible.
+
+
+ ⬇️ Scroll down until the clock is in view.
+
+
+
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/hooks/client-effect/solution/clock.css b/packages/docs/src/pages/tutorial/hooks/client-effect/solution/clock.css
new file mode 100644
index 00000000000..d1c742f7e38
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/client-effect/solution/clock.css
@@ -0,0 +1,92 @@
+/* Clock inspired by: https://paulund.co.uk/create-a-clock-in-css */
+
+.clock {
+ background: #fff;
+ border: 10px solid #7a7a7a;
+ border-radius: 50%;
+ box-sizing: border-box;
+ height: 300px;
+ margin: 0 auto;
+ position: relative;
+ width: 300px;
+}
+
+.twelve,
+.three,
+.six,
+.nine {
+ background: #333;
+ position: absolute;
+}
+
+.twelve,
+.six {
+ height: 10px;
+ width: 4px;
+}
+
+.three,
+.nine {
+ height: 4px;
+ width: 10px;
+}
+
+.twelve {
+ left: 50%;
+ top: -1px;
+}
+
+.three {
+ right: -1px;
+ top: 50%;
+}
+
+.six {
+ left: 50%;
+ bottom: -1px;
+}
+
+.nine {
+ left: -1px;
+ top: 50%;
+}
+
+.hour {
+ height: 60px;
+ width: 4px;
+ background: #333;
+ position: absolute;
+ left: 50%;
+ top: 80px;
+ animation: tick 43200s infinite linear;
+ -webkit-animation: tick 43200s infinite linear;
+}
+
+.minute {
+ height: 100px;
+ width: 4px;
+ background: #777;
+ position: absolute;
+ left: 50%;
+ top: 40px;
+ animation: tick 3600s infinite linear;
+ -webkit-animation: tick 3600s infinite linear;
+}
+
+.second {
+ height: 120px;
+ width: 3px;
+ background: #fc0505;
+ position: absolute;
+ left: 50%;
+ top: 20px;
+ animation: tick 60s infinite linear;
+ -webkit-animation: tick 60s infinite linear;
+}
+
+.hour,
+.minute,
+.second {
+ transform-origin: 2px 100%;
+ -webkit-transform-origin: 2px 100%;
+}
diff --git a/packages/docs/src/pages/tutorial/hooks/mount/index.mdx b/packages/docs/src/pages/tutorial/hooks/mount/index.mdx
new file mode 100644
index 00000000000..0c14074ffb3
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/mount/index.mdx
@@ -0,0 +1,30 @@
+---
+title: useMount$() Hook
+layout: tutorial
+---
+
+Use `useMount$()` to execute code when the component is mounted into the rendering tree. (Another way to think about it is that `useMount$()` executes on component creation.)
+
+## Component Life Cycle and SSR
+
+Qwik is resumable. Resumability means that the application starts up on the server and then the application is transferred to the client. On the client, the application continues execution from where it left off. The implication of this is that a component may be created on the server and destroyed on the client. This means that the component's `useMount$()` method may execute on the server and but its `useCleanup$()` method may execute on the client.
+
+## Usage of `useMount$()`
+
+`useMount$()` is a hook that executes a callback when the component is mounted into the rendering tree. The `useMount$()` function can be async. `useMount$()` delays the rendering of the component until the `useMount$()` callback is finished executing. Typical usage for `useMount$()` is to fetch data needed for initial rendering.
+
+## `useMethod$()` variations
+
+The `useMount$()` hook is subdivided into server and client versions:
+- `useServerMount$()`: Hook that executes on the component mount when in a server environment. This is useful because server often has different APIs for retrieving data.
+- `useClientMount$()`: Hook that executes on the component mount when in a client environment. This is useful because server often has different APIs for retrieving data.
+
+Use `useMount$()` if the code that needs to be executed is identical.
+
+## Server only imports
+
+Because `useServerMount$()` hooks have `$` in their name, they are subject to lazy loading. Lazy-loading means that the function is moved into a new file by the Optimizer. When the function is moved the Optimizer also moves any imports with it. This means that it is safe to have server-only imports as part of the `useServerMount$()` hook as they will be removed by the Optimizer.
+
+## Example
+
+Use `useServerMount()` to fetch data needed for the rendering.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/hooks/mount/problem/app.tsx b/packages/docs/src/pages/tutorial/hooks/mount/problem/app.tsx
new file mode 100644
index 00000000000..e3dafad2f4b
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/mount/problem/app.tsx
@@ -0,0 +1,39 @@
+import { component$, useServerMount$, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const github = useStore({
+ org: 'BuilderIO',
+ repos: null as string[] | null,
+ });
+
+ useServerMount$(async () => {
+ // Put code here to fetch data from the server.
+ });
+
+ return (
+
+
GitHub username: {github.org}
+
+ {github.repos ? (
+
+ {github.repos.map((repo) => (
+
+ {repo}
+
+ ))}
+
+ ) : (
+ 'loading...'
+ )}
+
+
+ );
+});
+
+export async function getRepositories(username: string, controller?: AbortController) {
+ const resp = await fetch(`https://api.github.com/users/${username}/repos`, {
+ signal: controller?.signal,
+ });
+ const json = await resp.json();
+ return Array.isArray(json) ? json.map((repo: { name: string }) => repo.name) : null;
+}
diff --git a/packages/docs/src/pages/tutorial/hooks/mount/solution/app.tsx b/packages/docs/src/pages/tutorial/hooks/mount/solution/app.tsx
new file mode 100644
index 00000000000..2d78f3c1787
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/hooks/mount/solution/app.tsx
@@ -0,0 +1,39 @@
+import { component$, useServerMount$, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const github = useStore({
+ org: 'BuilderIO',
+ repos: null as string[] | null,
+ });
+
+ useServerMount$(async () => {
+ github.repos = await getRepositories(github.org);
+ });
+
+ return (
+
+
GitHub username: {github.org}
+
+ {github.repos ? (
+
+ {github.repos.map((repo) => (
+
+ {repo}
+
+ ))}
+
+ ) : (
+ 'loading...'
+ )}
+
+
+ );
+});
+
+export async function getRepositories(username: string, controller?: AbortController) {
+ const resp = await fetch(`https://api.github.com/users/${username}/repos`, {
+ signal: controller?.signal,
+ });
+ const json = await resp.json();
+ return Array.isArray(json) ? json.map((repo: { name: string }) => repo.name) : null;
+}
diff --git a/packages/docs/src/pages/tutorial/host/basic/index.mdx b/packages/docs/src/pages/tutorial/host/basic/index.mdx
new file mode 100644
index 00000000000..fc09b1b2b34
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/host/basic/index.mdx
@@ -0,0 +1,25 @@
+---
+title: Host Element
+layout: tutorial
+---
+
+The host element is used to mark the component boundaries. Without the host element, Qwik would not know where components start and end. This information is needed so that components can render independently and out of order, a key feature of Qwik.
+
+An alternative to the host element is to recover the component boundaries by re-executing the application as part of hydration. Qwik's explicit goal is to not perform hydration on application startup as it would force eager application download and execution and negatively impact startup performance.
+
+## Controlling the Host Element Type with `tagName`
+
+Every component declared with `component$()` has a host element. By default the host element is `` marked with the `q:host` attribute. At times it may be necessary to change the `
` to another element, in which case the `tagName` option can be used with `component$()` as shown in this example.
+
+## `
` Element
+
+A component may need to add additional attributes or listeners to the host element. This can be done through the `` element. Any attribute that can be placed on the DOM element can be placed on the ``.
+
+
+## Example
+
+In this example, we have created `` component. This component needs to be a `` for accessibility. The example uses `tagName` to ensure that the host element is `button`.
+
+We need to place a click listener on the button, for this, we use `` element to refer to the host element.
+
+Please add styling to the `` to change the `background-color` to `lightpink` and make the button large by adding padding to it.
diff --git a/packages/docs/src/pages/tutorial/host/basic/problem/app.tsx b/packages/docs/src/pages/tutorial/host/basic/problem/app.tsx
new file mode 100644
index 00000000000..7298f4d082a
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/host/basic/problem/app.tsx
@@ -0,0 +1,20 @@
+import { component$, Host, QRL, Slot, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+ return store.count++}>{store.count} ;
+});
+
+interface MyButtonProps {
+ onClickQrl?: QRL<(event: Event) => void>;
+}
+export const MyButton = component$(
+ (props: MyButtonProps) => {
+ return (
+
+
+
+ );
+ },
+ { tagName: 'button' }
+);
diff --git a/packages/docs/src/pages/tutorial/host/basic/solution/app.tsx b/packages/docs/src/pages/tutorial/host/basic/solution/app.tsx
new file mode 100644
index 00000000000..261bb0655ca
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/host/basic/solution/app.tsx
@@ -0,0 +1,20 @@
+import { component$, Host, QRL, Slot, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+ return store.count++}>{store.count} ;
+});
+
+interface MyButtonProps {
+ onClickQrl?: QRL<(event: Event) => void>;
+}
+export const MyButton = component$(
+ (props: MyButtonProps) => {
+ return (
+
+
+
+ );
+ },
+ { tagName: 'button' }
+);
diff --git a/packages/docs/src/pages/tutorial/host/parent/index.mdx b/packages/docs/src/pages/tutorial/host/parent/index.mdx
new file mode 100644
index 00000000000..1614844ca41
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/host/parent/index.mdx
@@ -0,0 +1,31 @@
+---
+title: Binding to Host Element
+layout: tutorial
+---
+
+The host element is used to mark the component boundaries. Without the host element, Qwik would not know where components start and end. This information is needed so that components can render independently and out of order, a key feature of Qwik.
+
+
+## Binding to the Host Element
+
+In the previous example, we talked about how a component can be bound to its host element using `` element. It is also possible for the parent container to bind to the child components' host element. This can be done with the `host:` prefix.
+
+Let's imagine that we have a component ``. We can only set a property on a component if the component expects a property of that name. So how can we tell Qwik that we wish to bind to the HTML host element rather than a component?
+
+This can be achieved with the `host:` prefix. The `host:` tells Qwik to place the binding directly on the DOM host element, rather than as a prop of child component.
+
+```jsx
+
+```
+
+In the above example the:
+- `title="Component Title"` binds to the component props. (Assuming that the component has declared `title` as its input.)
+- `host:title="Host Element Title"` binds to the DOM host element's attribute `title`.
+
+The `host:` prefix can also be used for binding to the host element's events.
+
+## Example
+
+In the example on the right add:
+- style binding to change the background color of the ``
+- add a click implementation to the `host:onClick$` handler to increment `store.count` property.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/host/parent/problem/app.tsx b/packages/docs/src/pages/tutorial/host/parent/problem/app.tsx
new file mode 100644
index 00000000000..b7093166795
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/host/parent/problem/app.tsx
@@ -0,0 +1,21 @@
+import { component$, Host, Slot, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+ return (
+ null} host:style={{}}>
+ {store.count}
+
+ );
+});
+
+export const MyButton = component$(
+ () => {
+ return (
+
+
+
+ );
+ },
+ { tagName: 'button' }
+);
diff --git a/packages/docs/src/pages/tutorial/host/parent/solution/app.tsx b/packages/docs/src/pages/tutorial/host/parent/solution/app.tsx
new file mode 100644
index 00000000000..cd90291bdec
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/host/parent/solution/app.tsx
@@ -0,0 +1,24 @@
+import { component$, Host, Slot, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+ return (
+ store.count++}
+ host:style={{ backgroundColor: 'lightpink', padding: '1em' }}
+ >
+ {store.count}
+
+ );
+});
+
+export const MyButton = component$(
+ () => {
+ return (
+
+
+
+ );
+ },
+ { tagName: 'button' }
+);
diff --git a/packages/docs/src/pages/tutorial/introduction/ssr/index.mdx b/packages/docs/src/pages/tutorial/introduction/ssr/index.mdx
index 22c35dda9e9..aaff8e5fac6 100644
--- a/packages/docs/src/pages/tutorial/introduction/ssr/index.mdx
+++ b/packages/docs/src/pages/tutorial/introduction/ssr/index.mdx
@@ -7,7 +7,7 @@ In the previous tutorial, we added `useWatch$` to update the list of GitHub repo
When doing SSR, the server needs to know when it should take the snapshot (the rendering is completed) of the application and send it to the client. This is tricky because the server may need to fetch data that is then used in the response. The basic problem is that the server needs to know how long to delay the rendering of the component until the data is ready.
-The semantic of `useWatch$` is to run before rendering. This is good from the point of SSR as it allows the component to execute fetch before the component renders. The problem is that `useWatch$` can't block the rendering of the component. So if `useWatch$` is asynchronous, there is no way to delay the execution of component rendering. If we removed the early return for the server, the server would execute `useWatch$()`, that in turn will cause data to fetch. Because `useWatch$()` can't delay the rendering of the component, the Qwik would perform the rendering before the data returns and serialize the application into HTML. The result would be that the application would say `loading...`. It also means that the server did unnecessary fetch because it did not wait for the response. So `useWatch$` has the wrong semantics for what we want to achieve.
+The semantic of `useWatch$` is to run before rendering. This is good from the point of SSR as it allows the component to execute fetch before the component renders. The problem is that `useWatch$` can't block the rendering of the component. So if `useWatch$` is asynchronous, there is no way to delay the execution of component rendering. If we removed the early return for the server, the server would execute `useWatch$()`, that in turn will cause data to fetch. Because `useWatch$()` can't delay the rendering of the component, the Qwik would perform the rendering before the data returns and serialize the application into HTML without the response. The result would be that the application would say `loading...`. It also means that the server did unnecessary fetch because it did not wait for the response. So `useWatch$` has the wrong semantics for what we want to achieve.
For this reason, Qwik has `useServerMount$()`. The purpose of `useServerMount$()` is to execute before rendering and block the rendering until `useServerMount$()` completes. This allows `useServerMount$()` to fetch data and delay the rendering until the data is returned.
diff --git a/packages/docs/src/pages/tutorial/introduction/watch/index.mdx b/packages/docs/src/pages/tutorial/introduction/watch/index.mdx
index 77ef54c62a4..157263bef1c 100644
--- a/packages/docs/src/pages/tutorial/introduction/watch/index.mdx
+++ b/packages/docs/src/pages/tutorial/introduction/watch/index.mdx
@@ -3,16 +3,18 @@ title: Basics - Qwik Tutorial
layout: tutorial
---
-For this tutorial, we would like to fetch the list of repositories for a given GitHub organization. To aid you, we have added the `getRepositories()` function to the bottom of the file. Your task is to use the `getRepositories()` function to fetch the list of repositories whenever the user updates the `organization` input.
+For this tutorial, we would like to fetch the list of repositories for a given GitHub organization. To aid you, we have added the `getRepositories()` function to the bottom of the file. Your task is to use the `getRepositories()` function to fetch the list of repositories whenever the user updates the `org` input.
You will need to use `useWatch()` to observe changes to the `github.org`, and on each update of the organization, invoke `getRepositories()`. Here is an example of how to use `useWatch$()`
```typescript
useWatch$((track) => {
- // STEP 1: Tell Qwik which propreties it should track and re-run this function whenever they change.
+ // STEP 1: Tell Qwik which propreties it should track and
+ // re-run this function whenever they change.
track(github, "org");
- // STEP 2: For now, we don't want to run this code on the server. We will cover SSR in the later part.
+ // STEP 2: For now, we don't want to run this code on the
+ // server. We will cover SSR in the later part.
if (isServer) return;
// STEP 3: Perform the fetching of the repositories.
@@ -22,7 +24,8 @@ useWatch$((track) => {
(repos) => (github.repos = repos)
);
- // STEP 4: Return a cleanup function to abort the fetching if the user changes the organization.
+ // STEP 4: Return a cleanup function to abort the fetching
+ // if the user changes the organization.
return () => controller.abort();
});
```
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/introduction/watch/problem/app.tsx b/packages/docs/src/pages/tutorial/introduction/watch/problem/app.tsx
index 909a4c02553..66eb823cb04 100644
--- a/packages/docs/src/pages/tutorial/introduction/watch/problem/app.tsx
+++ b/packages/docs/src/pages/tutorial/introduction/watch/problem/app.tsx
@@ -41,3 +41,13 @@ export async function getRepositories(username: string, controller?: AbortContro
const json = await resp.json();
return Array.isArray(json) ? json.map((repo: { name: string }) => repo.name) : null;
}
+
+export const isServer = (() => {
+ // This is a workaround for a bug in REPL which fails to package up of @builder.io/qwik/build.
+ // TODO remove this when the bug is fixed.
+ try {
+ return typeof window == 'undefined';
+ } catch (e) {
+ return false;
+ }
+})();
diff --git a/packages/docs/src/pages/tutorial/introduction/watch/solution/app.tsx b/packages/docs/src/pages/tutorial/introduction/watch/solution/app.tsx
index 711dbcac502..ff8edf884ce 100644
--- a/packages/docs/src/pages/tutorial/introduction/watch/solution/app.tsx
+++ b/packages/docs/src/pages/tutorial/introduction/watch/solution/app.tsx
@@ -1,5 +1,5 @@
import { component$, useStore, useWatch$ } from '@builder.io/qwik';
-import { isServer } from '@builder.io/qwik/build';
+// import { isServer } from '@builder.io/qwik/build';
export const App = component$(() => {
const github = useStore({
@@ -52,3 +52,13 @@ export async function getRepositories(username: string, controller?: AbortContro
const json = await resp.json();
return Array.isArray(json) ? json.map((repo: { name: string }) => repo.name) : null;
}
+
+export const isServer = (() => {
+ // This is a workaround for a bug in REPL which fails to package up of @builder.io/qwik/build.
+ // TODO remove this when the bug is fixed.
+ try {
+ return typeof window == 'undefined';
+ } catch (e) {
+ return false;
+ }
+})();
diff --git a/packages/docs/src/pages/tutorial/projection/basic/index.mdx b/packages/docs/src/pages/tutorial/projection/basic/index.mdx
new file mode 100644
index 00000000000..38233c01f2c
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/basic/index.mdx
@@ -0,0 +1,22 @@
+---
+title: Projection
+layout: tutorial
+---
+
+Projection is a way of passing content to a child component that in turn controls where the content is rendered. Projection is a collaboration between the parent and child component. The parent component decides what is the content that needs to be rendered, child component decides where and if the content should be rendered.
+
+In our example, the content of the `` element (inside the ``) is the content that needs to be projected. The `` component wraps the content in a `` tag and should project it using the `
` element.
+
+## Why ``?
+
+Why is Qwik opting to use ` ` rather than `children` property? The use of `` is a strategic choice to enable the rendering of components out of order. (Meaning that a component should be capable of re-rendering even if the parent component is not yet resumed.) There are two issues with using `children` in Qwik.
+1. If Qwik would use the `children` property for projection the `children` property would need to be serializable. This is because when the child component renders it needs the `children` to insert someplace.
+2. A child component could modify the contents of the `children` before passing and inserting it into the render tree. This would prevent the parent component from rendering independently from the child. If a child would modify the children, then every time the parent component would change the `children` Qwik would have to re-run the child component to see what kind of modifications it would make to the `children` before inserting them into the render tree.
+
+For Qwik, the `` approach is preferable because it declaratively controls the content and location of the projection. This allows the parent component to change the projection content, without forcing the child component to re-render.
+
+## Example
+
+Change the `` component to project the `` content using the `` element.
+
+Notice that the `` is re-render on button click, but the `` is not re-rendered on interaction. This is because ` ` is declarative and allows Qwik to know where the content should be projected even if `` is not loaded.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/projection/basic/problem/app.tsx b/packages/docs/src/pages/tutorial/projection/basic/problem/app.tsx
new file mode 100644
index 00000000000..e491313030e
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/basic/problem/app.tsx
@@ -0,0 +1,22 @@
+/* eslint-disable no-console */
+import { component$, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+ console.log('Render: ');
+ return (
+
+ Count: {store.count}. store.count++}>+1
+
+ );
+});
+
+export const Panel = component$(() => {
+ console.log('Render: ');
+ return (
+
+ Currently the <Panel> component controls the content here. Replace this text
+ with <Slot> element to see the content projected from the <App> .
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/projection/basic/solution/app.tsx b/packages/docs/src/pages/tutorial/projection/basic/solution/app.tsx
new file mode 100644
index 00000000000..7d29bfe5470
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/basic/solution/app.tsx
@@ -0,0 +1,21 @@
+/* eslint-disable no-console */
+import { component$, Slot, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+ console.log('Render: ');
+ return (
+
+ Count: {store.count}. store.count++}>+1
+
+ );
+});
+
+export const Panel = component$(() => {
+ console.log('Render: ');
+ return (
+
+
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/projection/fallback/index.mdx b/packages/docs/src/pages/tutorial/projection/fallback/index.mdx
new file mode 100644
index 00000000000..d065204b2b1
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/fallback/index.mdx
@@ -0,0 +1,10 @@
+---
+title: Fallback Content
+layout: tutorial
+---
+
+Fallback content allows the child component to display fallback content in case the parent component did not provide content. Fallback content is declared inside the `` element.
+
+## Example
+
+In this example, we show three cards, but some of them are not filled with content. Use the `` element to specify the fallback content.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/projection/fallback/problem/app.tsx b/packages/docs/src/pages/tutorial/projection/fallback/problem/app.tsx
new file mode 100644
index 00000000000..5a7857abae9
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/fallback/problem/app.tsx
@@ -0,0 +1,61 @@
+import { component$, Host, Slot, useStyles$ } from '@builder.io/qwik';
+
+export const Card = component$(() => {
+ useStyles$(CSS);
+ return (
+
+
+
+
+
+
+
+
+ );
+});
+
+export const App = component$(() => {
+ return (
+ <>
+
+ Qwik
+ Qwik is a resumable framework for building instant web apps.
+
+
+ Partytown
+
+
+
+ Builder.io allows you to visually build on your tech stack Empower your entire team to
+ visually create and optimize high-speed experiences on your sites and apps. Provide
+ whole-team autonomy with a platform that is developer approved.
+
+
+ >
+ );
+});
+
+export const CSS = `
+.card {
+ border-radius: 5px;
+ vertical-align: top;
+ display: inline-block;
+ border: 1px solid grey;
+ width: 200px;
+ margin: .5em;
+}
+
+.title {
+ background-color: lightgray;
+ padding: 0.5em;
+ border-bottom: 1px solid black;
+}
+
+q\\:fallback {
+ color: gray;
+}
+
+.body {
+ padding: 0.5em;
+}
+`;
diff --git a/packages/docs/src/pages/tutorial/projection/fallback/solution/app.tsx b/packages/docs/src/pages/tutorial/projection/fallback/solution/app.tsx
new file mode 100644
index 00000000000..39663cc8ae3
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/fallback/solution/app.tsx
@@ -0,0 +1,61 @@
+import { component$, Host, Slot, useStyles$ } from '@builder.io/qwik';
+
+export const Card = component$(() => {
+ useStyles$(CSS);
+ return (
+
+
+ (no title)
+
+
+ (no content)
+
+
+ );
+});
+
+export const App = component$(() => {
+ return (
+ <>
+
+ Qwik
+ Qwik is a resumable framework for building instant web apps.
+
+
+ Partytown
+
+
+
+ Builder.io allows you to visually build on your tech stack Empower your entire team to
+ visually create and optimize high-speed experiences on your sites and apps. Provide
+ whole-team autonomy with a platform that is developer approved.
+
+
+ >
+ );
+});
+
+export const CSS = `
+.card {
+ border-radius: 5px;
+ vertical-align: top;
+ display: inline-block;
+ border: 1px solid grey;
+ width: 200px;
+ margin: .5em;
+}
+
+.title {
+ background-color: lightgray;
+ padding: 0.5em;
+ border-bottom: 1px solid black;
+}
+
+q\\:fallback {
+ color: gray;
+}
+
+.body {
+ padding: 0.5em;
+}
+`;
diff --git a/packages/docs/src/pages/tutorial/projection/slots/index.mdx b/packages/docs/src/pages/tutorial/projection/slots/index.mdx
new file mode 100644
index 00000000000..3960406a468
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/slots/index.mdx
@@ -0,0 +1,14 @@
+---
+title: Named Slots
+layout: tutorial
+---
+
+In simple cases, projection allows content from the parent component to be projected into the child component. In more complex cases there may be more than one content slot that needs to be projected. Having multiple content slots is achieved by naming them.
+
+## Example
+
+In this example, we have created `` component that toggles between open and closed states. Currently, when the `` is closed it does not show any content. Add additional `` to project the `q:slot="closed"` content.
+
+## Unprojected Slots
+
+We have added console statements to show when individual components re-render. Notice that the `` component never re-renders on the client. Also notice that the `` projects only one content at a time. This means that when `` renders on the server it has to produce both default as well as `closed` content that Qwik must serialize. The benefit is that when `` toggles between open and closed states, it does not need to re-render the `` component to recover the content that was projected into it.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/projection/slots/problem/app.tsx b/packages/docs/src/pages/tutorial/projection/slots/problem/app.tsx
new file mode 100644
index 00000000000..834e8252b2d
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/slots/problem/app.tsx
@@ -0,0 +1,33 @@
+import { component$, Slot, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ console.log('Render: ');
+ return (
+
+ (collapsed summary)
+ Content that should be displayed when the collapse component is open.
+
+ );
+});
+
+export const Collapsable = component$(() => {
+ console.log('Render: ');
+ const store = useStore({ open: true });
+ return (
+ (store.open = !store.open)}>
+ {store.open ? (
+
+ ) : (
+
+ ▶︎
+ {/* Project content name "closed" here */}
+
+ )}
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/projection/slots/solution/app.tsx b/packages/docs/src/pages/tutorial/projection/slots/solution/app.tsx
new file mode 100644
index 00000000000..db6de966a75
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/projection/slots/solution/app.tsx
@@ -0,0 +1,33 @@
+import { component$, Slot, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ console.log('Render: ');
+ return (
+
+ (collapsed summary)
+ Content that should be displayed when the collapse component is open.
+
+ );
+});
+
+export const Collapsable = component$(() => {
+ console.log('Render: ');
+ const store = useStore({ open: true });
+ return (
+ (store.open = !store.open)}>
+ {store.open ? (
+
+ ) : (
+
+ ▶︎
+
+
+ )}
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/props/basic/index.mdx b/packages/docs/src/pages/tutorial/props/basic/index.mdx
new file mode 100644
index 00000000000..32e245b248c
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/basic/index.mdx
@@ -0,0 +1,8 @@
+---
+title: Component Props
+layout: tutorial
+---
+
+Web applications are built up from components in the same way that general applications are built up from functions. Composing functions would not be very useful if functions would not allow us to pass parameters to them. In the same way that functions have parameters components have props. We use props to pass data from parent to child components.
+
+Modify the code so that the parent `` can pass `salutation="Hello"` and `name="World"` to the ``. Add properties to the `GreeterProps` interface and then add the necessary bindings.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/props/basic/problem/app.tsx b/packages/docs/src/pages/tutorial/props/basic/problem/app.tsx
new file mode 100644
index 00000000000..0e3223d836c
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/basic/problem/app.tsx
@@ -0,0 +1,14 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+
+
+
+ );
+});
+
+interface GreeterProps {}
+export const Greeter = component$((props: GreeterProps) => {
+ return Bind props here
;
+});
diff --git a/packages/docs/src/pages/tutorial/props/basic/solution/app.tsx b/packages/docs/src/pages/tutorial/props/basic/solution/app.tsx
new file mode 100644
index 00000000000..b83a5896fd8
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/basic/solution/app.tsx
@@ -0,0 +1,21 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+
+
+
+ );
+});
+
+interface GreeterProps {
+ salutation: string;
+ name: string;
+}
+export const Greeter = component$((props: GreeterProps) => {
+ return (
+
+ {props.salutation} {props.name}!
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/props/closures/index.mdx b/packages/docs/src/pages/tutorial/props/closures/index.mdx
new file mode 100644
index 00000000000..fba4f185f33
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/closures/index.mdx
@@ -0,0 +1,51 @@
+---
+title: Passing Closures
+layout: tutorial
+---
+
+Props must be serializable so that Qwik can resume and render each component independently from other components on the page. This possess a problem if we wish to pass a callback to a child component. Callbacks are functions and functions are not directly serializable, but they are serializable through the `$()` by converting them to QRLs first.
+
+## QRLs
+
+Function passing across serializable boundaries must be done through QRLs. QRLs are serialized forms of a function. (See [QRL](/docs/advanced/qrl) in advanced section.)
+
+Qwik has convenience APIs which end in `$` that are equivalent to calling `$()` directly. These two lines are equivalent:
+- inline: ` alert('click')}/>`
+- explicit: ` alert('click'))}/>`
+
+Most of the time we use the first form as it allows us to inline our callbacks directly into the API. But at times it is necessary to use the second form so that we can separate where the function is declared and where it is used.
+
+## Declaring callback props
+
+A component can declare a callback in its props by:
+- Property that ends in `Qrl` (as in `goodbyeQrl`)
+- The type of the property is `QRL` where `T` is the lazy reference type that the QRL points to (function signature).
+
+The TypeScript automatically creates a `$:T` property for every `Qrl:QRL` property. For example `helloQrl: QRL<(name: string) => void>` becomes `hello$: (name: string) => void` property.
+
+This allows the user of `` to use either the `Qrl` or `$` form as shown here:
+```jsx
+ {...}} />
+```
+
+## Using callback props
+
+Notice that the `` component can bind either directly to the prop QRL or wrap the QRL prop in another callback.
+
+Passing the `props.goodbyeQrl` as a reference to the ``
+```jsx
+hello
+```
+
+Creating a new callback for `` and internally invoking the callback QRL.
+```jsx
+ {
+ await props.helloQrl?.invoke('World');
+ }}>good bye
+```
+
+This form allows the `` to invoke the callback with custom parameters. Notice that the invocation requires `async` and `await` as the QRLs are lazy-loaded.
+
+## Understanding QRLs
+
+Notice that it is only the creator of API that needs to understand QRLs. The user of the API can be oblivious to the fact that callbacks get converted into QRLs and that they need to be invoked by asynchronous means. As long as the user of API sticks to the properties ending in `$` no special knowledge is required.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/props/closures/problem/app.tsx b/packages/docs/src/pages/tutorial/props/closures/problem/app.tsx
new file mode 100644
index 00000000000..e5ec9fe5cd7
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/closures/problem/app.tsx
@@ -0,0 +1,23 @@
+import { component$, $, QRL } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const goodbyeQrl = $(() => alert('Good Bye!'));
+ return (
+
+ alert('Hello ' + name)} />
+
+ );
+});
+
+interface MyComponentProps {
+ goodbyeQrl?: QRL<() => void>;
+ helloQrl?: QRL<(name: string) => void>;
+}
+export const MyComponent = component$((props: MyComponentProps) => {
+ return (
+
+ hello
+ await props.helloQrl?.invoke('World')}>good bye
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/props/closures/solution/app.tsx b/packages/docs/src/pages/tutorial/props/closures/solution/app.tsx
new file mode 100644
index 00000000000..e5ec9fe5cd7
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/closures/solution/app.tsx
@@ -0,0 +1,23 @@
+import { component$, $, QRL } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const goodbyeQrl = $(() => alert('Good Bye!'));
+ return (
+
+ alert('Hello ' + name)} />
+
+ );
+});
+
+interface MyComponentProps {
+ goodbyeQrl?: QRL<() => void>;
+ helloQrl?: QRL<(name: string) => void>;
+}
+export const MyComponent = component$((props: MyComponentProps) => {
+ return (
+
+ hello
+ await props.helloQrl?.invoke('World')}>good bye
+
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/props/mutable/index.mdx b/packages/docs/src/pages/tutorial/props/mutable/index.mdx
new file mode 100644
index 00000000000..97096984d69
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/mutable/index.mdx
@@ -0,0 +1,28 @@
+---
+title: Mutable Props
+layout: tutorial
+---
+
+Look at the example on the right. It is a simple counter. The unique part about this counter is that the displaying of the count was broken out into a separate component called ``.
+
+When the user clicks on the `+1` button the `store.count` increments. This causes the `` component to re-render which in turn causes the `` component to re-render.
+
+But it does not work, because by default Qwik assumes that all property bindings are static. There is no way for Qwik runtime to tell if a property is static or it can change during the application lifetime. Qwik runtime assumes that all properties are static unless they are marked `mutable()`.
+
+## Why assume that properties are static
+
+Imagine for a second that the `` is bound to a static value like so:
+
+```jsx
+
+```
+
+Looking at this code we can tell that `` will never need to be re-render from the outside. But when Qwik is serializing `` it does not know if the properties are static or dynamic (such information is not available at runtime). If it assumes that they are dynamic then it has to serialize the props all the time. If it assumes they are static then it only needs to serialize the props if the child component itself has its own behavior.
+
+Because Qwik wants to minimize the amount of HTML sent to the client, it is better to assume that the properties are static. But this presents a problem. How do we tell Qwik, that in this case, they are actually dynamic? We do this by wrapping the mutable binding in `mutable()` function call which provides the necessary runtime information to Qwik.
+
+You can fix the code example by changing `store.count` to `mutable(store.count)`.
+
+## Its better to not use `mutable()`
+
+See the next step in the tutorial on how we can avoid using `mutable()`.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/props/mutable/problem/app.tsx b/packages/docs/src/pages/tutorial/props/mutable/problem/app.tsx
new file mode 100644
index 00000000000..ae90c8e6550
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/mutable/problem/app.tsx
@@ -0,0 +1,22 @@
+import { component$, useStore } from '@builder.io/qwik';
+
+interface CountStore {
+ count: number;
+}
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+
+ return (
+ <>
+ store.count++}>+1
+
+ >
+ );
+});
+
+interface DisplayProps {
+ count: number;
+}
+export const Display = component$((props: DisplayProps) => {
+ return The count is: {props.count}
;
+});
diff --git a/packages/docs/src/pages/tutorial/props/mutable/solution/app.tsx b/packages/docs/src/pages/tutorial/props/mutable/solution/app.tsx
new file mode 100644
index 00000000000..f07eccfbe9a
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/mutable/solution/app.tsx
@@ -0,0 +1,22 @@
+import { component$, useStore, mutable } from '@builder.io/qwik';
+
+interface CountStore {
+ count: number;
+}
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+
+ return (
+ <>
+ store.count++}>+1
+
+ >
+ );
+});
+
+interface GreeterProps {
+ count: number;
+}
+export const Display = component$((props: GreeterProps) => {
+ return The count is: {props.count}
;
+});
diff --git a/packages/docs/src/pages/tutorial/props/store/index.mdx b/packages/docs/src/pages/tutorial/props/store/index.mdx
new file mode 100644
index 00000000000..742dc200057
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/store/index.mdx
@@ -0,0 +1,20 @@
+---
+title: Passing Stores
+layout: tutorial
+---
+
+In the previous tutorial, we have shown that Qwik assumes that all props are static for efficiency reasons. The previous example introduced you to `mutable()` which told Qwik that the props are mutable.
+
+Besides the fact that typing `mutable()` is extra work, the previous example is also inefficient. When the user clicks on the `+1` it causes the `` to be re-rendered so it can update the `` bindings. The re-rendering of `` is needed to update the props of `` but there is no update to what the user sees, so it is a waste of resources.
+
+It would be more efficient to only re-render `` component. We can do that by passing in the `CountStore` rather than the `count` value. Because the store reference never changes the `` component will not need to re-render.
+
+Change the code to pass int `store` rather than `store.count`.
+
+By making the above change we gain two benefits:
+- we remove `mutable()` from our component.
+- `` does not need to be downloaded or re-rendered.
+
+## Best Practice
+
+It is considered best practice in Qwik to pass the store to the child component rather than pass the individual primitives which will require to be wrapped in `mutable()` to make the application work.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/props/store/problem/app.tsx b/packages/docs/src/pages/tutorial/props/store/problem/app.tsx
new file mode 100644
index 00000000000..40dba76a418
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/store/problem/app.tsx
@@ -0,0 +1,22 @@
+import { component$, useStore, mutable } from '@builder.io/qwik';
+
+interface CountStore {
+ count: number;
+}
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+
+ return (
+ <>
+ store.count++}>+1
+
+ >
+ );
+});
+
+interface DisplayProps {
+ count: number;
+}
+export const Display = component$((props: DisplayProps) => {
+ return The count is: {props.count}
;
+});
diff --git a/packages/docs/src/pages/tutorial/props/store/solution/app.tsx b/packages/docs/src/pages/tutorial/props/store/solution/app.tsx
new file mode 100644
index 00000000000..23be1e88770
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/props/store/solution/app.tsx
@@ -0,0 +1,22 @@
+import { component$, useStore } from '@builder.io/qwik';
+
+interface CountStore {
+ count: number;
+}
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+
+ return (
+ <>
+ store.count++}>+1
+
+ >
+ );
+});
+
+interface DisplayProps {
+ store: CountStore;
+}
+export const Display = component$((props: DisplayProps) => {
+ return The count is: {props.store.count}
;
+});
diff --git a/packages/docs/src/pages/tutorial/qrl/closures/index.mdx b/packages/docs/src/pages/tutorial/qrl/closures/index.mdx
new file mode 100644
index 00000000000..a3270211e53
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/closures/index.mdx
@@ -0,0 +1,14 @@
+---
+title: Lazy Lading Closures
+layout: tutorial
+---
+
+A closure can be converted into a lazy-loaded reference using the `$()` function. This generates a `QRL` type. A QRL is a lazy-loadable reference of the closure. In our case, we have extracted the closure associated with the `onKeyUp` event into the component body. Because it is no longer inlined we need to change how the JSX refers to it from `onKeyUp$` to `onKeyUpQrl`.
+
+Notice that our closure closes over the `store` that is captured by the Optimizer and then restored as needed.
+
+## Example
+
+In this example, we would like to demonstrate how easy it is to lazy-load behavior in Qwik. Let's lazy load the code that is executed when the `Enter` key is pressed. Wrap the code associated with `Enter` with `$()` to mark it for lazy loading. The resulting `QRL<()=>void>` can then be lazy-invoked with `.invoke()` and the result can be awaited with `await` keyword.
+
+Look at the `Symbols` tab to see how the code was broken up into pieces.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/qrl/closures/problem/app.tsx b/packages/docs/src/pages/tutorial/qrl/closures/problem/app.tsx
new file mode 100644
index 00000000000..c30f74a5dfd
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/closures/problem/app.tsx
@@ -0,0 +1,18 @@
+import { component$, useStore, $ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ name: '' });
+ const onKeyUp = $(async (event: KeyboardEvent) => {
+ const input = event.target as HTMLInputElement;
+ if (event.key === 'Enter') {
+ alert(store.name);
+ } else {
+ store.name = input.value;
+ }
+ });
+ return (
+ <>
+ Enter your name followed by the enter key:
+ >
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/qrl/closures/solution/app.tsx b/packages/docs/src/pages/tutorial/qrl/closures/solution/app.tsx
new file mode 100644
index 00000000000..039e0546d10
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/closures/solution/app.tsx
@@ -0,0 +1,20 @@
+import { component$, useStore, $ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ name: '' });
+ const onKeyUp = $(async (event: KeyboardEvent) => {
+ const input = event.target as HTMLInputElement;
+ if (event.key === 'Enter') {
+ await $(() => {
+ alert(store.name);
+ }).invoke();
+ } else {
+ store.name = input.value;
+ }
+ });
+ return (
+ <>
+ Enter your name followed by the enter key:
+ >
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/qrl/data/index.mdx b/packages/docs/src/pages/tutorial/qrl/data/index.mdx
new file mode 100644
index 00000000000..1bc5c1fdf19
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/data/index.mdx
@@ -0,0 +1,17 @@
+---
+title: Loading Data
+layout: tutorial
+---
+
+The Optimizer can be used to lazy-load data, not just functions. The benefit of doing that is that lazy loading any part of your application becomes easy.
+
+Use the stand-alone `$()` function to mark the data that you wish to lazy load. The `$()` function returns a `QRL` that is a reference that can be serialized by Qwik and that can later be resolved into the original value.
+
+## Example
+
+Load the data representing the massage lazily as part of the click listener.
+
+For this exercise imagine that we wish to lazy load the `'Hello World!`' string. Use the `$()` function to mark the string as lazy-loadable. Then use `await to resolve the lazy-loadable value.
+
+
+Go to the `Symbols` tab and examine how the code was broken down into parts.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/qrl/data/problem/app.tsx b/packages/docs/src/pages/tutorial/qrl/data/problem/app.tsx
new file mode 100644
index 00000000000..4ddffb22cc6
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/data/problem/app.tsx
@@ -0,0 +1,9 @@
+import { component$ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+ <>
+ alert('Hello World!')}>click me
+ >
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/qrl/data/solution/app.tsx b/packages/docs/src/pages/tutorial/qrl/data/solution/app.tsx
new file mode 100644
index 00000000000..58eeb049f3c
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/data/solution/app.tsx
@@ -0,0 +1,9 @@
+import { component$, $ } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ return (
+ <>
+ alert(await $('Hello World!').resolve())}>click me
+ >
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/qrl/optimizer/index.mdx b/packages/docs/src/pages/tutorial/qrl/optimizer/index.mdx
new file mode 100644
index 00000000000..ae460fef528
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/optimizer/index.mdx
@@ -0,0 +1,22 @@
+---
+title: Optimizer
+layout: tutorial
+---
+
+For the application to be resumable it needs to have lots of entry points. For example, clicking on button `A` is a different entry point than clicking on button `B`. When we implement an application we don't usually think about entry points and so we typically end up with just one entry point or the `main()` function.
+
+The Optimizer does its job by looking for functions that end in `$` character. For example, the Optimizer will transform a call to `component$()` into an entry point. Notice that the name of the function doesn't matter only that it ends with the `$`.
+
+Every time you see `$` you should think, there is a lazy-loaded boundary here. The implication is that the lazy-loaded content may require lazy-loading and hence can't be accessed synchronously.
+
+While the Optimizer can serialize any data that Qwik can serialize, it has special handling for closures. Closures are functions that are created inside of other functions and that may capture variables in the lexical scope. The ability to serialize closures is a key property that makes Qwik resumable. Without closure serialization, it would be difficult to have resumable applications.
+
+## Example
+
+In this example notice that we have two lazy-loaded chunks because we have two `$` in our code.
+
+Open the `Symbols` tab and notice how Optimizer turned the `onClick$` function into an entry point. Specifically, notice that the `onClick$` entry point does not import the `@builder.io/qwik` module.
+
+Now change the `onClick$` callback to `store.count++`.
+
+Open the `Symbols` tab again and notice that this time the Optimizer imported `@builder.io/qwik` and inserted `useLexicalScope()` call to restore the captured state of the event handler. Restoring the captured state of the function is what makes Qwik resumable.
diff --git a/packages/docs/src/pages/tutorial/qrl/optimizer/problem/app.tsx b/packages/docs/src/pages/tutorial/qrl/optimizer/problem/app.tsx
new file mode 100644
index 00000000000..d1dbcf1cb7d
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/optimizer/problem/app.tsx
@@ -0,0 +1,11 @@
+/* eslint-disable no-console */
+import { component$, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+ return (
+ <>
+ Count: {store.count} console.log('+1')}>+1
+ >
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/qrl/optimizer/solution/app.tsx b/packages/docs/src/pages/tutorial/qrl/optimizer/solution/app.tsx
new file mode 100644
index 00000000000..4ae1580d34e
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/qrl/optimizer/solution/app.tsx
@@ -0,0 +1,11 @@
+/* eslint-disable no-console */
+import { component$, useStore } from '@builder.io/qwik';
+
+export const App = component$(() => {
+ const store = useStore({ count: 0 });
+ return (
+ <>
+ Count: {store.count} store.count++}>+1
+ >
+ );
+});
diff --git a/packages/docs/src/pages/tutorial/reactivity/explicit/index.mdx b/packages/docs/src/pages/tutorial/reactivity/explicit/index.mdx
new file mode 100644
index 00000000000..76f4b17efff
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/reactivity/explicit/index.mdx
@@ -0,0 +1,14 @@
+---
+title: Explicit Reactivity
+layout: tutorial
+---
+
+In addition to implicit reactivity created by the templates, Qwik supports explicit execution of code when a property changes. This is achieved through the `useWatch$()` hook. `useWatch$()` hooks execute before the component renders and can be asynchronous. The hook can also have a clean-up function that is invoked on the next hook execution or when the component is removed.
+
+In this example clicking on `+1` updates `count` immediately. What we would like is to update the `delay count` after a 2-second delay. If `count` is updated before the 2 seconds are up then the timer is restarted.
+
+Notice that `useWatch$()` callback receives `track` function. Use the `track` function to tell Qwik which properties should trigger this watch. The `track` function creates subscriptions in store. On each invocation of `useWatch$()` the subscriptions are cleared, so it is important to always set up a new set of subscriptions. This is useful if the set of subscriptions changes during the function lifetime.
+
+The `useWatch$()` callback function can return a cleanup function. The clean-up function is invoked on the next `useWatch$()` callback execution or when the component is removed. In our case, the cleanup function is used for returning code which clears the `setTimeout`.
+
+The `useWatch$()` callbacks execute before the component is rendered. This allows them to be used to compute values used in rendering. The function runs on both server and client. The server execution sets up subscriptions that are then serialized and available to the client. This saves the client from having to download all of the components and execute them at least once to recover the subscription information for the system.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/reactivity/explicit/problem/app.tsx b/packages/docs/src/pages/tutorial/reactivity/explicit/problem/app.tsx
new file mode 100644
index 00000000000..eef4f53ef8b
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/reactivity/explicit/problem/app.tsx
@@ -0,0 +1,38 @@
+/* eslint-disable no-console */
+import { component$, useStore, useWatch$ } from '@builder.io/qwik';
+
+interface AppStore {
+ count: number;
+ delayCount: number;
+}
+export const App = component$(() => {
+ const store = useStore({
+ count: 0,
+ delayCount: 0,
+ });
+ console.log('Render: ');
+ useWatch$((track) => {
+ // tracking `store.count`
+ // setup a timer to copy `count => delayCount` after 2 seconds.
+ return () => {
+ // cleanup code
+ };
+ });
+ return (
+ <>
+
+
+ store.count++}>+1
+ >
+ );
+});
+
+export const DisplayCount = component$((props: { store: AppStore }) => {
+ console.log('Render: ');
+ return <>{props.store.count}>;
+});
+
+export const DisplayDelayCount = component$((props: { store: AppStore }) => {
+ console.log('Render: ');
+ return <>{props.store.delayCount}>;
+});
diff --git a/packages/docs/src/pages/tutorial/reactivity/explicit/solution/app.tsx b/packages/docs/src/pages/tutorial/reactivity/explicit/solution/app.tsx
new file mode 100644
index 00000000000..9d540d15e23
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/reactivity/explicit/solution/app.tsx
@@ -0,0 +1,36 @@
+/* eslint-disable no-console */
+import { component$, useStore, useWatch$ } from '@builder.io/qwik';
+
+interface AppStore {
+ count: number;
+ delayCount: number;
+}
+export const App = component$(() => {
+ const store = useStore({
+ count: 0,
+ delayCount: 0,
+ });
+ console.log('Render: ');
+ useWatch$((track) => {
+ track(store, 'count');
+ const id = setTimeout(() => (store.delayCount = store.count), 2000);
+ return () => clearTimeout(id);
+ });
+ return (
+ <>
+
+
+ store.count++}>+1
+ >
+ );
+});
+
+export const DisplayCount = component$((props: { store: AppStore }) => {
+ console.log('Render: ');
+ return <>{props.store.count}>;
+});
+
+export const DisplayDelayCount = component$((props: { store: AppStore }) => {
+ console.log('Render: ');
+ return <>{props.store.delayCount}>;
+});
diff --git a/packages/docs/src/pages/tutorial/reactivity/template/index.mdx b/packages/docs/src/pages/tutorial/reactivity/template/index.mdx
new file mode 100644
index 00000000000..e9c9419e6ed
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/reactivity/template/index.mdx
@@ -0,0 +1,16 @@
+---
+title: Implicit Template Updates
+layout: tutorial
+---
+
+This example demonstrates how mutating stores automatically update the templates.
+
+During SSR rendering the server needs to render all of the components in the application. As it is rendering the components the bindings in those components perform reads on store properties. For example, when `` reads the `countA` property from the store, Qwik records that as a subscription. Qwik now knows that if `countA` changes then `` needs to be re-rendered. Rendering templates will automatically set up subscriptions on the store. Each time the template re-renders the old subscriptions are thrown away and new subscriptions are created. This means that the template can change the set of things it is listening to during its lifecycle.
+
+Currently, the buttons don't do anything. Implement the buttons to increment the respective store properties.
+
+Once you make the buttons work, notice that even though all state is stored in a single store, the updates are very focused. `a++` button will only cause the re-rendering of `` and `b++` button will only cause re-rendering of ``. The fine-grained re-rendering is an important property of Qwik. It is what allows Qwik applications to stay lean and not download too much code unnecessarily.
+
+Template subscriptions are automatically created and released when the component is removed. There is no need to keep track of them or release them manually.
+
+Qwik is a reactive system. All reactive systems require a single full execution of the application to create subscriptions. Qwik applications also require full execution to set up all subscriptions. However, Qwik applications perform the full execution on the server and transfer the subscription information to the client. In this way, the client knows which component needs to be re-rendered when without being forced to do one full rendering of the whole application. Doing so would force all components to be eagerly downloaded, and Qwik wants to avoid that.
\ No newline at end of file
diff --git a/packages/docs/src/pages/tutorial/reactivity/template/problem/app.tsx b/packages/docs/src/pages/tutorial/reactivity/template/problem/app.tsx
new file mode 100644
index 00000000000..df7e6ae95e4
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/reactivity/template/problem/app.tsx
@@ -0,0 +1,33 @@
+/* eslint-disable no-console */
+import { component$, useStore } from '@builder.io/qwik';
+
+interface AppStore {
+ countA: number;
+ countB: number;
+}
+export const App = component$(() => {
+ const store = useStore({
+ countA: 0,
+ countB: 0,
+ });
+ console.log('Render: ');
+ return (
+ <>
+ null}>a++
+
+
+ null}>b++
+
+ >
+ );
+});
+
+export const DisplayA = component$((props: { store: AppStore }) => {
+ console.log('Render: ');
+ return <>{props.store.countA}>;
+});
+
+export const DisplayB = component$((props: { store: AppStore }) => {
+ console.log('Render: ');
+ return <>{props.store.countB}>;
+});
diff --git a/packages/docs/src/pages/tutorial/reactivity/template/solution/app.tsx b/packages/docs/src/pages/tutorial/reactivity/template/solution/app.tsx
new file mode 100644
index 00000000000..129ba8c096b
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/reactivity/template/solution/app.tsx
@@ -0,0 +1,33 @@
+/* eslint-disable no-console */
+import { component$, useStore } from '@builder.io/qwik';
+
+interface AppStore {
+ countA: number;
+ countB: number;
+}
+export const App = component$(() => {
+ const store = useStore({
+ countA: 0,
+ countB: 0,
+ });
+ console.log('Render: ');
+ return (
+ <>
+ store.countA++}>a++
+
+
+ store.countB++}>b++
+
+ >
+ );
+});
+
+export const DisplayA = component$((props: { store: AppStore }) => {
+ console.log('Render: ');
+ return <>{props.store.countA}>;
+});
+
+export const DisplayB = component$((props: { store: AppStore }) => {
+ console.log('Render: ');
+ return <>{props.store.countB}>;
+});
diff --git a/packages/docs/src/pages/tutorial/store/basic/index.mdx b/packages/docs/src/pages/tutorial/store/basic/index.mdx
new file mode 100644
index 00000000000..a46a4a6e28b
--- /dev/null
+++ b/packages/docs/src/pages/tutorial/store/basic/index.mdx
@@ -0,0 +1,22 @@
+---
+title: Storing State
+layout: tutorial
+---
+
+Applications need to store state to be useful. (Otherwise, they are just static pages.)
+
+Qwik needs to track the application state for two reasons:
+1. Application state needs to be serialized on application pause (and deserialize on application resume.)
+2. Qwik needs to create subscriptions on stores so that it can know which components need to be re-rendered (if Qwik would not track subscription information then it would have to re-render the whole application which would defeat the lazy-loading.)
+
+The component on the right looks like it should work, but it does not because the `counter` is just a regular objects, which does not create subscriptions. As a result, Qwik does not know when `counter.count` changes and it does not know that it has to re-render the ``.
+
+Wrap the `counter` in `useStore()` to enable dependency tracking and automatic re-rendering.
+
+## Serialization
+
+Open the HTML tab to see what information was serialized by the server. find `