---
title: QRL | Advanced
contributors:
  - ahhshm
  - manucorporat
  - adamdbradley
  - literalpie
  - the-r3aper7
  - mhevery
  - wtlin1228
  - bebraw
  - mrhoodz
updated_at: '2023-10-12T18:28:54Z'
created_at: '2023-03-20T23:45:13Z'
---

# QRL

QRL (Qwik URL) is a particular form of URL that Qwik uses to lazy load content.

QRLs:

- are specially formatted URLs that are left as attributes in the HTML to tell Qwik where the handlers for the code should be loaded from.
- point to a JavaScript chunk to be lazy-loaded.
- Contain a symbol name which needs to be retrieved from the chunk.
- May contain lexically scoped object references. (Captured variables from closures.)
- If relative, use `q:base` for resolution.

## QRL Encoding

```
./path/to/chunk.js#SymbolName
```

In its simplest form, the QRL contains a URL (such as `./path/to/chunk.js`) that the browser can use to lazy-load a resource, and a `SymbolName` to retrieve from the lazy-loaded chunk.

Qwik uses `q:base` to resolve a QRL into an absolute URL if the URL is relative. (If no `q:base` attribute is present, then `document.baseURI` is used as a base.)

## Encoding lexically scoped captured variables

QRLs can also restore lexically scoped variables. In that case, the variables are encoded in the QRL at the end in the form of an array of indexes into the `q:obj` attribute.

```
./path/to/chunk.js#SymbolName[0,1]
```

The array is used by `useLexicalScope()` to restore the variables.

### Example

Let's look at an example of how all of the pieces of the QRL tie together.

The developer writes code for a simple component.

```tsx
export const Counter = component$((props: { step: number }) => {
  const count = useSignal(0);

  return <button onClick$={() => (count.value += props.step || 1)}>{count.value}</button>;
});
```

The optimizer breaks above code into pieces like so:

```tsx
const Counter = component(qrl('./chunk-a.js', 'Counter_onMount'));
```

```tsx title="chunk-a.js"
export const Counter_onMount = (props) => {
  const count = useSignal(0);
  return qrl('./chunk-b.js', 'Counter_onRender', [count, props]);
};
```

```tsx title="chunk-b.js"
const Counter_onRender = () => {
  const [count, props] = useLexicalScope();
  return (
    <button onClick$={qrl('./chunk-c.js', 'Counter_onClick', [count, props])}>{count.value}</button>
  );
};
```

```tsx title="chunk-c.js"
const Counter_onClick = () => {
  const [count, props] = useLexicalScope();
  return (count.value += props.step || 1);
};
```

### Rendered HTML

After the above code gets executed, it produces this HTML.

Assume: `http://localhost/index.html`

```html
<html>
  <body q:base="/build/">
    <button q:obj="456, 123" on:click="./chunk-c.js#Counter_onClick[0,1]">0</button>
    <script>
      /*Qwikloader script*/
    </script>
    <script type="qwik/json">
      {...json...}
    </script>
  </body>
</html>
```

The main thing to note is the `on:click` attribute. This attribute gets read by the Qwikloader when the user clicks on the button.

1. HTML is loaded in the browser, and Qwikloader registers a global `click` listener. No other JavaScript is loaded/executed at this point.
2. User clicks on the `<button>`. This fires a `click` event which bubbles up and is handled by the Qwikloader.
3. Qwikloader retraces the event bubble path and looks for `on:click` attribute, which it finds on `<button>`.
4. Qwikloader now tries to load the corresponding chunk. To do that, the Qwikloader needs to resolve the relative path of `./chunk-c.js`. It uses these values to build an absolute path starting at `<button>` and walking towards the document.
   - `on:click="./chunk-c.js#Counter_onClick[0,1]"`
   - `<body q:base="/build/">`
   - `document.baseURI = "http://localhost/index.html"`
   - The resulting absolute URL is `http://localhost/build/chunk-c.js` which Qwikloader fetches.
5. Qwikloader now retrieves the `Counter_onClick` reference from `http://localhost/build/chunk-c.js` and invokes it.
   ```tsx
   const Counter_onClick = () => {
     const [count, props] = useLexicalScope();
     return (count.value += props.step || 1);
   };
   ```
6. At this point, the execution is handed off from Qwikloader to the lazy-loaded chunk. This is done so that the Qwikloader can be as small as possible as it is inlined into the HTML.
7. `useLexicalScope` is imported from `@builder.io/qwik` and is responsible for retrieving the `count` and `props`.
   `const [count, props] = useLexicalScope();`
8. Parse the `<script type="qwik/json">{...json...}</script>` JSON and distribute the deserialized objects per `q:obj` attribute. In our case
   - `<div q:id="123" q:obj="456" q:host>` gets object with id `123`. This will be the `count` created in the `Counter_onMount` function.
   - `<button q:obj="456, 123"` gets `count` as well as a reference to the `<div q:id="457">`
9. Once the `qwik/json` is deserialized, `useLexicalScope` can use the QRL's `[0,1]` array to look into `q:obj="456, 123"` to retrieve object with id `456` and `123`, which are props of `<div q:id="123" q:obj="456" q:host>` as well as `store` from `Counter_onMount` function.

> **NOTE:** For performance reasons the `q:obj` and `<script type="qwik/json">` are only updated when the application is deserialized into HTML. When the application is running, these attributes may have stale values.

## Why not just dynamic `import()`?

Browsers already have dynamic import mechanisms available from `import()`. Why not use that instead of inventing a new QRL format?

There are several reasons:

1. For Qwik to work, the QRLs need to be serialized into HTML. This is problematic for dynamic `import()` because there is no easy way to retrieve the relative URL from the `import('./some-path.js')` so that it can be placed in the HTML.
2. Dynamic imports deal with chunks, but they have no mechanism to refer to a specific symbol in the chunk.
3. Dynamic imports have relative paths to the file which imported it. This is a problem because when the relative path is placed in HTML, it loses the context which defines relative to what. When the framework reads the path from HTML and tries to import it as `import(element.getAttribute('on:click'))`, the framework will become the context for relative path resolution. This is wrong as the original context before serialization to the HTML was different.
4. QRLs encode information about the lexically scoped variables that were captured in the closure and need to be restored.
5. Dynamic import 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 the ability of the tooling to move code around in an automated way.

Because of the above differences, Qwik introduces QRLs as a mechanism for lazy loading closures into a Qwik application.

## See Also

- [`qrl`: API used to create QRLs.](https://qwik.dev/api/qwik/#qrl)
