Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@qwikdev/astro

Package Overview
Dependencies
Maintainers
5
Versions
107
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@qwikdev/astro

Use Qwik components and Resumability within Astro

  • 0.3.8
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
630
increased by39.07%
Maintainers
5
Weekly downloads
 
Created
Source

@qwikdev/astro 💜

Leverage the power of Resumability inside of Astro, using Qwik components.

Installation

There are two methods to add the integration. Let's begin with the easiest one!

The Astro CLI

Astro comes with a command-line tool for incorporating built-in integrations: astro add. This command will:

  1. Optionally install all required dependencies and peer dependencies
  2. Optionally modify your astro.config.* file to apply the integration

To install @qwikdev/astro, run the following from your project directory and follow the prompts:

# Using NPM
npx astro add @qwikdev/astro

# Using Yarn
yarn astro add @qwikdev/astro

# Using PNPM
pnpm astro add @qwikdev/astro

Setting up the TypeScript Config

The integration needs the following in tsconfig.json for typescript to recognize Qwik's JSX types.

"compilerOptions": {
  "jsx": "react-jsx",
  "jsxImportSource": "@builder.io/qwik"
}
When Qwik isn't the primary jsxImportSource

If you don't intend to use Qwik as your primary jsxImportSource, add:

/** @jsxImportSource @builder.io/qwik */

at the top of each Qwik component file.

This is when you may not have that many Qwik components compared to other JSX frameworks on the page.

If you face any issues, please post them on Github and attempt the manual installation below.

Manual Installation

First, install the @qwikdev/astro integration like so:

npm install @qwikdev/astro

Typically, package managers install peer dependencies. However, if you get a Cannot find package '@builder.io/qwik' warning when starting Astro, install Qwik.

npm install @builder.io/qwik

Now, add the integration to your astro.config.* file using the integrations property:

  // astro.config.mjs
  import { defineConfig } from 'astro/config';
+ import qwikdev from '@qwikdev/astro';

  export default defineConfig({
    // ...
    integrations: [qwikdev()],
    //             ^^^^^
  });

Key differences

Hooray! We now have our integration installed. Before deep diving in, there are quite a few differences than other UI frameworks.

Qwik does not hydrate, it is fundamentally different

Astro is popular for its partial hydration approach, whereas Qwik does not require hydration.

What does this mean?

Qwik components do not need hydration directives

In other UI frameworks, a hydration directive would be needed for interactivity, such as client:only or client:load. These are not needed with Qwik, because there is no hydration!

When using Qwik inside a meta framework like Astro or Qwik City, components are loaded on the server, prefetched in a separate thread, and "resumed" on the client.

For example here's how we create a counter component in Qwik (e.g. at src/components/counter.tsx).

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

export const Counter = component$(() => {
  const counter = useSignal(0);

  return <button onClick$={() => counter.value++}>{counter.value}</button>;
});

It can be consumed in our index.astro page like so:

    ---
    import { Counter } from "../components/counter";
    ---

    <html lang="en">
        <head>
            <meta charset="utf-8" />
            <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
            <meta name="viewport" content="width=device-width" />
            <meta name="generator" content={Astro.generator} />
            <title>Astro</title>
        </head>
        <body>
            <h1>Astro.js - Qwik</h1>
            /* no hydration directive! */
            <Counter />
        </body>
    </html>

Let's take a look at this in the wild.

A gif showing a button clicked and the onClick$ resumed

Here we are refreshing the page, and you'll notice nothing was executed until the button was clicked. Without resumability, our <Counter /> would have been executed on page load.

The 402 byte q-chunk is our Counter's onClick$ handler.

What's in that 17.61kb chunk?

The framework! We do not execute it until it is needed. In this case it is gzipped using SSG.

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.

Resumability vs. Hydration chart

Instead, upon page load, a tiny 1kb minified piece of JavaScript, known as the Qwikloader, downloads the rest of the application as needed.

Fine-grained lazy loading

Hydration forces your hand to eagerly execute code. 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.

Containers vs. Islands

While Astro generally adopts an islands architecture with other frameworks, Qwik uses a different strategy known as Qwik containers. Despite the differences in approach, both share similar limitations.

An example of a Qwik container

In the DOM, you'll 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?

Other frameworks with Astro address this by using nano stores.

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

  • Micro Frontend (MFE) Support
  • Different versions can exist on the page
  • Survives serialization (unlike nano stores)
  • Performance (avoid unnecessary state synchronization)
  • Event Driven
  • Decoupled

Using multiple JSX frameworks

Qwik works with other JSX frameworks out of the box. It should not need an include or exclude property.

We've noticed some slight edge cases with other renderers, and so we suggest adding Qwik to the beginning of your integrations array.

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

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

If there is a newer JSX framework integration other than React, Preact, or Solid you may need to add an include or exclude keyword on the qwik integration.

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, you can use qwik-react instead. The proper configuration will be supported out of the box.

/** @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

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

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

index.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

Contributing

We'd love for you to contribute! Start by reading our Contributing Guide. It's got all the info you need to get involved, including an in-depth section on how the integration works under the hood.

There's also a qwik-astro channel in the builder.io discord to discuss API changes, possible ideas to the integration, and other cool stuff. 😊

Credits

Special thanks to Matthew and Nate from the Astro core team! This integration would not be possible without their help.

Nate's handles:

Keywords

FAQs

Package last updated on 13 Jan 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc