|
| 1 | +# Qwik Framework - LLM Context Documentation |
| 2 | + |
| 3 | +This documentation helps LLMs understand the Qwik framework's architecture, development patterns, and best practices for effective code generation and assistance. Be sure to include this documentation in the context of conversations. |
| 4 | + |
| 5 | +Qwik is a modern web framework focused on instant-loading web applications with resumable server-side rendering and precision lazy-loading. It's designed to achieve the fastest possible page load times regardless of application complexity. |
| 6 | + |
| 7 | +Its programming model is very similar to React, but with a few important differences that will be explained in this documentation. |
| 8 | + |
| 9 | +Qwik Router (AKA qwik-city) is a full-stack meta-framework built on Qwik. It is designed to be used with Qwik, and it is the recommended way to build web applications with Qwik. It is a file-based router that is designed to be used with Qwik's server-side rendering. |
| 10 | + |
| 11 | +## Core Concepts |
| 12 | + |
| 13 | +- **Automatic Optimization**: Qwik is designed to be optimized automatically. |
| 14 | + - The developer should focus on writing code, and the framework will take care of the rest. |
| 15 | + - There is a build-time optimizer that is responsible for optimizing the application. It runs as a Vite plugin. |
| 16 | +- **Resumable Server-side Rendering**: Unlike traditional frameworks that replay, Qwik resumes execution from where the server left off. |
| 17 | + - The result of SSR is called a **container** and it is generally a full HTML document, but it could also be a partial HTML document (e.g. a `<div>`), to be included in a larger HTML document. |
| 18 | + - SSR always succeeds, and there is never a mismatch between the server and client. |
| 19 | + - Application state is automatically tree-shaken, serialized and deserialized between the server and client. A state management framework is not needed. |
| 20 | + - There is _no_ single client entry point. The application does _not_ hydrate, it **resumes** from where the server left off. |
| 21 | + - No JavaScript is needed for initial page interactivity, except for the small, automatically added, `qwikloader` script. |
| 22 | +- **Precision Lazy-loading**: Only necessary parts of the application load on-demand as users interact. |
| 23 | + - This is comparable to streaming a video, where only the segments that are needed are downloaded, as the user watches them. |
| 24 | + - Code is automatically split into the smallest possible **segments**. |
| 25 | + - This means that every event handler is a potential application entry point. |
| 26 | + - There is a `preloader` script that is automatically added to the page, which loads the most likely segments before they are needed. |
| 27 | +- **Reactive State Management**: Qwik uses Signals and Stores to manage application state. |
| 28 | + - A **Signal** is an object with a `value` property, which automatically subscribes readers to changes. |
| 29 | + - A **Store** is an object with custom properties, which automatically subscribes readers to changes. |
| 30 | + - When a Signal or Store changes, the relevant parts of the application are automatically re-rendered. The optimizer ensures that changes only affect the smallest part of the application possible. |
| 31 | + |
| 32 | +## Writing Code |
| 33 | + |
| 34 | +- Write code in TypeScript. |
| 35 | +- When creating a new component, use the `component$()` wrapper. Sometimes, you can use an "Inline Component" which is a non-wrapped Component. This removes access to hooks, and disables automatic code splitting. |
| 36 | +- To store state, prefer using Signals over Stores. |
| 37 | +- It is ok to destructure the `props` object, it will not interfere with optimization. |
| 38 | + |
| 39 | +## Key Files |
| 40 | + |
| 41 | +- `vite.config.ts` - The Vite configuration file, which includes the `qwikVite` and `qwikCity` plugins and their respective configurations. |
| 42 | +- `src/root.tsx` - The root component of the application. |
| 43 | +- `src/entry.server.tsx` - The server entry point of the application. It can also be called `src/entry.fastify.tsx` etc. |
| 44 | +- `src/entry.ssr.tsx` - This exports a function that is called by the server to render the application. |
| 45 | +- `src/components/` - The recommended place to put shared components. |
| 46 | +- `public/` - Static assets. They will be copied as-is to the output directory. For example `robots.txt`, `favicon.ico` or `manifest.json`. |
| 47 | + |
| 48 | +### Routing |
| 49 | + |
| 50 | +- `src/routes/` - The routes of the application. |
| 51 | +- `src/routes/**/layout.tsx` - The layout wrapping the route. It should export a default component that renders the `<Slot />` component. It can also contain handlers, routeLoaders and `head` declarations. |
| 52 | +- `src/routes/**/index.tsx` - The page component of the route. It should export a default component that renders the page for the current route. |
| 53 | +- `src/routes/**/*` - Supporting files for the current route. |
| 54 | +- `src/routes/plugin@<name>.ts` - Middleware that can export handlers for the route. They will be called in alphabetical order of the file name. |
| 55 | + |
| 56 | +## Architecture Patterns |
| 57 | + |
| 58 | +Write code in TypeScript. Write types when they cannot be inferred. |
| 59 | + |
| 60 | +For clarity, always dereference Signals when passing them to children. The optimizer will automatically proxy them for performance. |
| 61 | + |
| 62 | +When something should only run on the client or server, guard the code with `if (isBrowser) { ... }` or `if (isServer) { ... }`. These are exported from `@builder.io/qwik`. |
| 63 | + |
| 64 | +Server-side code: |
| 65 | + |
| 66 | +- When you need to access the server, use `server$()`. Inside a server function, verify the used parameters as well as the any variables used from upper scopes, as they are provided by the calling client. |
| 67 | +- Prefer using helper functions that are defined in separate files dedicated to server-side code. |
| 68 | +- Avoid directly exposing database structures to the client, instead use wrapping functions that return the data in a format suitable for the client. |
| 69 | + |
| 70 | +### Compared to React |
| 71 | + |
| 72 | +Progamming is very similar to React, but there are some important differences: |
| 73 | + |
| 74 | +- There are no class components. |
| 75 | +- Instead of `useState()`, use `useSignal()`. |
| 76 | +- Instead of `useReducer()`, use `useSignal()` or `useStore()`. |
| 77 | +- Instead of `useMemo()`, use `useComputed$()`. |
| 78 | +- For context, use `createContextId()`, `useContextProvider()`, and `useContext()`. You can `useContext()` in the same component that first provides the context. |
| 79 | +- Instead of `useEffect()` and `useLayoutEffect()`, use `useTask$()` or `useVisibleTask$()`. |
| 80 | +- Instead of `useRef()` and `useImperativeHandle()`, use `useSignal()` and optionally put a function in the `ref` attribute. |
| 81 | +- `useCallback()` is not needed, but you can wrap any function with `$()` to make it a single-instance lazy-loadable. When you pass a function to a child component into a prop ending in `$`, it will be wrapped with `$()` automatically. |
| 82 | + |
| 83 | +Generally speaking, a component will only render once, unless its props change, or it uses reactive state in the render body. Reactive state in the JSX return will be proxied to the children or the DOM. |
| 84 | + |
| 85 | +You can directly return Promises in JSX, but the render function itself should not be async because that interferes with reactivity detection. |
| 86 | + |
| 87 | +### Component Structure |
| 88 | + |
| 89 | +- Components are functions that return JSX, as defined in the type `JsxOutput`. |
| 90 | +- Always use `component$()` wrapper. |
| 91 | + - This removes `children`, which you have to render instead by outputting the `<Slot />` component in the right place(s). |
| 92 | + - Exception: if you need to read `children` you can use an "Inline Component" which is a non-wrapped Component. This removes access to hooks, and disables automatic code splitting. |
| 93 | +- Prefer to put the props type after the first argument of the component function, either inline or in a separate interface. This makes it easier to switch between inline and wrapped components. |
| 94 | +- Event handlers always use `...$()` suffix for lazy loading. For example `onClick$={() => { ... }}`. In general, DOM events are to be handled with `onEventName$={(ev, el) => { ... }}`, where `ev` is the event object and `el` is the element that triggered the event. |
| 95 | +- Event handlers are called async when they are not loaded yet and sync otherwise. This means that `ev.preventDefault()` will not always work. So instead, use the attributes `preventdefault:eventname` and `stoppropagation:eventname` to prevent the default behavior and stop the event from bubbling up. For example `preventdefault:click` to prevent the default click behavior and `stoppropagation:click` to stop the click event from bubbling up. |
| 96 | + |
| 97 | +### State Management |
| 98 | + |
| 99 | +- `useSignal()` - Reactive primitive values. This is the preferred way to store state. |
| 100 | +- `useStore()` - Reactive object store. Use this to store complex state with deep reactivity. |
| 101 | +- `useComputed$()` - Reactive computed values. This is the preferred way to derive values from state. |
| 102 | +- `useResource$()` - Async data fetching. |
| 103 | +- `useTask$()` - Side effects and lifecycle. |
| 104 | +- `useVisibleTask$()` - Side effects and lifecycle. Use this to run side effects that are not possible to run on the server, but be aware that it can slow down the initial load. Consider passing `{strategy: 'document-idle'}` to run it after the page is loaded. |
| 105 | + |
| 106 | +#### Tasks |
| 107 | + |
| 108 | +Tasks are run in the order of their declaration, and are not concurrent. As long as no Task returns a Promise, they will run during the render function. |
| 109 | + |
| 110 | +However, once a Task returns a Promise, subsequent Tasks will wait for the Promise to resolve before running, and the render function will complete first. |
| 111 | + |
| 112 | +You can have async Tasks that change state during SSR, and the server will wait for them to complete before sending the response. You cannot change the JSX of a parent component via state changes during SSR, because the parent component will already have started streaming out. |
| 113 | + |
| 114 | +### Routing (Qwik City) |
| 115 | + |
| 116 | +- File-based routing in `src/routes/` |
| 117 | +- Layouts with `layout.tsx`. Make sure to render `<Slot />`. |
| 118 | +- `server$()` automatically calls the server to execute the given function and returns the result. Use this where normally you would use an internal REST API. |
| 119 | +- Loaders with `routeLoader$()` for data fetching. This data starts to be fetched before SSR starts, based on where in the routes the loader is exported. |
| 120 | +- Actions with `routeAction$()` for form handling. |
| 121 | +- Folder naming for routes, in order of precedence: |
| 122 | + - `(internalName)`: purely for code organization. The folder is read and becomes part of the current route |
| 123 | + - regular string: exact match for the given string |
| 124 | + - `[paramName]`: match any string up to the next `/`, and store it under `params[paramName]` of the request object (available in server functions) and `useLocation()`. |
| 125 | + - `[...restName]`: match any subpath |
| 126 | + |
| 127 | +## Testing Strategy |
| 128 | + |
| 129 | +- Unit tests with Vitest |
| 130 | +- Component testing with @builder.io/qwik/testing |
| 131 | +- E2E tests with Playwright |
| 132 | +- Target 80% test coverage |
| 133 | +- TDD approach recommended |
| 134 | + |
| 135 | +## Code Style |
| 136 | + |
| 137 | +- TypeScript strict mode enabled |
| 138 | +- ESLint with custom Qwik rules |
| 139 | +- Prettier for formatting |
| 140 | +- JSDoc comments for public APIs |
| 141 | +- Consistent file naming conventions |
0 commit comments