Skip to content

Commit

Permalink
docs(astro): update integration doc (#5771)
Browse files Browse the repository at this point in the history
  • Loading branch information
thejackshelton committed Jan 26, 2024
1 parent fe594a8 commit 9e2d332
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 5 deletions.
2 changes: 1 addition & 1 deletion packages/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
"packageManager": "[email protected]",
"private": true,
"scripts": {
"prebuild.core": "tsm check-qwik-build.ts",
"build": "qwik build",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
Expand All @@ -74,6 +73,7 @@
"deploy": "wrangler pages publish ./dist",
"dev": "tsm check-qwik-build.ts && vite --mode ssr --open",
"dev.debug": "node --inspect-brk ../../node_modules/vite/bin/vite.js --mode ssr --force",
"prebuild.core": "tsm check-qwik-build.ts",
"preview": "qwik build preview && vite preview --open",
"preview.only": "NODE_DEBUG=net,http node --inspect-brk ../../node_modules/vite/bin/vite.js preview",
"preview.wrangler": "wrangler pages dev ./dist",
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/repl/worker/repl-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const initReplServer = (win: Window, doc: Document, nav: Navigator) => {
iframe.classList.add('loading');
iframe.src = `/repl/` + result.clientId + `/`;
iframe.dataset.buildId = String(result.buildId);
iframe.setAttribute("sandbox", "allow-popups allow-modals allow-scripts allow-same-origin")
iframe.setAttribute('sandbox', 'allow-popups allow-modals allow-scripts allow-same-origin');

iframe.addEventListener('load', () => {
if (!iframe.nextElementSibling) {
Expand Down
216 changes: 213 additions & 3 deletions packages/docs/src/routes/docs/integrations/astro/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ It also allows you to write components using your favorite UI framework (or no f

This results in a fast, SEO-friendly output that can be deployed to any static hosting environment or server.

> For more details on the integration and to view the source code, check out the [QwikDev Astro Integration on GitHub](https://github.com/QwikDev/astro).
## Astro instead of Qwik City

When integrating Astro with Qwik, it's important to note that Qwik City APIs are not compatible with Astro.
Expand Down Expand Up @@ -143,11 +145,218 @@ It can be consumed in our `index.astro` page like so:
</html>
```

### Roadmap
## Starts fast, stays fast

One of Astro's key features is **Zero JS, by default**. Unfortunately, after adding a JavaScript framework, and any subsequent components this is usually not the case.

If we want to introduce interactivity with a framework such as React, Vue, Svelte, etc., the framework runtime is then introduced. The number of components added to the page also increases linearly O(n) with the amount of JavaScript.

### Astro + Qwik

Qwik builds on top of Astro's **Zero JS, by defaut** principle and then some. Thanks to resumability, the components are not executed unless resumed. Even with interactivity, the framework is also not executed until it needs to be. It is O(1) constant, and zero effort on the developer.

Instead, upon page load, a tiny 1kb minified piece of JavaScript, known as the [Qwikloader](https://qwik.builder.io/docs/advanced/qwikloader/), downloads the rest of the application as needed.

### Fine-grained lazy loading

Hydration forces your hand [to eagerly execute code](https://www.builder.io/blog/hydration-sabotages-lazy-loading). It's not a problem with components that are outside of the tree, such as modals, but it must exhaustively check each component in the render tree just in case.

Qwik works exceptionally well in Astro due to Resumability and its ability to lazy load code in a fine-grained manner. Especially for marketing sites, blogs, and content oriented sites with many components.

### Instant interactivity

As of `@qwikdev/astro` v0.4, we have added support for [Speculative Module Fetching](https://qwik.builder.io/docs/advanced/speculative-module-fetching/) in Astro.

This enables instant interactivity for your Qwik components. Speculative module fetching will prefetch the application bundles in the background of a service worker, so that when needed, the code is already present in the browser cache.

> You should be able to use [Qwik insights](https://qwik.builder.io/docs/labs/insights/) out of the box!
## Containers vs. Islands

While Astro generally adopts an islands architecture with other frameworks, Qwik uses a different strategy known as [Qwik containers](https://qwik.builder.io/docs/advanced/containers/). Despite the differences in approach, both share similar limitations.

In the DOM, you may notice there aren't any `<astro-island>` custom elements, this is because to Astro, Qwik looks like static data.

> This is because in Qwik, the handlers themselves are the roots / entrypoints of the application.
### Communicating across containers

One common limitation is trying to pass state into another island or container.

Sharing state is crucial in modern web development. The question is, how can we achieve this when state needs to be shared across different containers or islands?

#### Why not use global signals or nanostores?

Other frameworks with Astro address this by using [nano stores](https://github.com/nanostores/nanostores), or [global signals](https://www.solidjs.com/tutorial/stores_nocontext).

While you may see all of your tests passing, and the application working as expected, we do not recommend using nanostores or global signals. They can lead to some unexpected behavior in an SSR context.

For example, in Solid's tutorial the following is mentioned:

> While it is possible to use global state and computations, Context is sometimes a better solution. Additionally, it is important to note that global state should not be used in SSR (server side rendering) solutions, such as Solid Start. On the server, global state is shared across requests, and the lack of data isolation can (and will) lead to bugs, memory leaks and has security implications. It is recommended that application state should always be provided via context instead of relying on global.
#### Custom Events

In Qwik, it was a design decision to not include global signal state.

Instead, we recommend the use of **custom events**, which offer several advantages:

- Performance (avoid unnecessary state synchronization)
- Does not wake up the framework on page load
- Micro Frontend (MFE) Support
- Different versions can exist on the page
- Event Driven
- Decoupled

[This example](https://github.com/thejackshelton/astro-qwik-global-state-example/blob/main/src/components/counter.tsx) shows how custom events can be used throughout your application. Pay attention to `counter.tsx`, `random-island.tsx`, and our `index.astro` page.

## Using multiple JSX frameworks

To use multiple JSX frameworks like Qwik, React, Preact, or Solid in Astro, you need to set up rules for which files each framework should handle.

For example, you can place all Qwik components in a folder named `qwik`. Then, configure Astro to process any file within this folder using the Qwik integration.

```tsx
import { defineConfig } from "astro/config";
import qwik from "@qwikdev/astro";
import react from "@astrojs/react";

export default defineConfig({
integrations: [
qwik({ include: "**/qwik/*" }),
react({ include: "**/react/*" }),
solid({ include: "**/solid/*" }),
],
});
```

Above we're using the Qwik, React, and Solid integrations in the same Astro project.

If we look at the first integration, it's going to look for any file in the `qwik` folder and use Qwik for any file in this folder.

For simplicity, consider grouping common framework components in the same folder (like `/components/react/` and `/components/qwik/`). However, this is optional.

### Qwik React

If you're using React, we suggest using the `@builder.io/qwik-react` integration. It's a drop-in replacement for `@astrojs/react`, and allows a seamless transition to Qwik.

```tsx
import { defineConfig } from "astro/config";

import qwikdev from "@qwikdev/astro";
import { qwikReact } from "@builder.io/qwik-react/vite";

// https://astro.build/config
export default defineConfig({
integrations: [qwikdev()],
vite: {
plugins: [qwikReact()],
},
});
```

With Qwik-React, we can "qwikify" our React components, and use them in our Qwik application, even nesting Qwik and React components outside of an Astro file!

There are some things missing from Astro that we would like to add in the future. That being better [prefetching](https://qwik.builder.io/docs/advanced/modules-prefetching/#prefetching-modules) and [Insights](https://qwik.builder.io/docs/labs/insights/).
> You do not need to specify an include property with qwikReact.
If there's anything else you think would be awesome with Astro & Qwik, feel free to take a crack at it.
[Here's an example](https://github.com/thejackshelton/qwik-react-astro-template) of a React component with the `qwik-react` integration.

```tsx
/** @jsxImportSource react */
import { qwikify$ } from "@builder.io/qwik-react";
import { useState } from "react";

const ReactCounter = () => {
const [count, setCount] = useState(0);

return <button onClick={() => setCount(count + 1)}>React {count}</button>;
};

// "Qwikified" React component
export const QReactCounter = qwikify$(ReactCounter);
```

After creating our counter, it can be consumed in our [index.astro](https://github.com/thejackshelton/qwik-react-astro-template/blob/main/src/pages/index.astro) file.

```tsx
<QReactCounter qwik:visible />
```

Notice that in `.astro` files we use a `qwik:` hydration directive prefix, this is to prevent a conflict with Astro's hydration directives that are provided out of the box.

You can also use the `client:*` prefix, but only in tsx files. You can find a list of directives in [Adding Interactivity](https://qwik.builder.io/docs/integrations/react/#adding-interactivity) section of the Qwik docs.

> Qwik React components still have hydration, thus it is recommended to use Qwik-React as a migration strategy to resumable components.
### jsxImportSource

Unfortunately, TypeScript can only have one `jsxImportSource` default. If you're using React, Solid, or Preact's Astro integration in your Astro app alongside, please override each component's import source.

> If you're using [@astrojs/react](https://www.npmjs.com/package/@astrojs/react), you can use [qwik-react](https://qwik.builder.io/docs/integrations/react/#qwik-react-%EF%B8%8F) instead. The proper configuration will be supported out of the box.
```tsx
/** @jsxImportSource react */
import { useState } from "react";

export const ReactCounter = () => {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
};
```

Solid JS for example, is:

```
/** @jsxImportSource solid-js */
```

Preact for example, is:

```
/** @jsxImportSource preact */
```

## Named Slots

For named slots within Astro, instead of adding `q:slot` on the markup, add `slot` instead.

**my-slot-comp.tsx**

```tsx
import { Slot, component$, useSignal } from "@builder.io/qwik";

export const MySlotComp = component$<{ initial: number }>((props) => {
return (
<>
<Slot name="test" />
</>
);
});
```

**index.astro**

```astro
<MySlotComp>
<div slot="test">Content inside the slot named test!</div>
</MySlotComp>
```

Default slots work as expected in their Qwik City counterpart.

## Community Guides

- [Paul Scanlon](https://www.paulie.dev/) shows a hands-on look at [using Qwik in Astro over React and Vanilla JS](https://thenewstack.io/how-quiks-astro-integration-beats-both-react-and-vanilla-js/).

- [Rishi Raj Jain](https://twitter.com/rishi_raj_jain_) has written an awesome guide on setting up Qwik with Astro's [Vercel SSR Adapter](https://dev.to/reeshee/qwik-look-at-resumability-with-astro-on-vercel-44fj).

- [Paul Scanlon](https://www.paulie.dev/) explores using [Qwik as a React alternative](https://thenewstack.io/take-a-qwik-break-from-react-with-astro/) in Astro.

## Videos

- Watch Jason & Steve [discuss the Qwik Astro integration] on the [LWJ show](https://www.youtube.com/@learnwithjason).

- [Awesome's Qwik + Astro video](https://www.youtube.com/watch?v=wKvkYUNBa5k) goes into how Astro just got even faster.

## Contributing

Expand All @@ -160,6 +369,7 @@ There's also a `qwik-astro` channel in the builder.io discord to discuss API cha
Special thanks to Matthew and Nate from the Astro core team! This integration would not be possible without their help.

Nate's handles:

- [Twitter](https://twitter.com/n_moore)
- [GitHub](https://github.com/natemoo-re)

Expand Down

0 comments on commit 9e2d332

Please sign in to comment.