Is Qwik Faster than React Server Component?

Is Qwik Faster than React Server Component?

TL;DR

  • 🔥 We'll find out whether the trending framework Qwik is faster than Next.js
  • 🤖 We'll look at a demo that generates with OpenAI's DALL-E and its performance benchmark.
  • 🚀 We'll discuss how Next.js leverages React server component to achieve fast SSR.

This article is also available on

Feel free to read it on your favorite platform


Page speed matters. Faster website results in better UX, better SEO, and more profit. The latest research done by Rekuten 24 shows that optimizing Core Web Vitals leads to:

  • 53.37% in revenue per visitor.
  • 33.13% in conversion rate.
  • 15.20% in average order value.
  • 9.99% in average time spent.
  • A 35.12% reduction in exit rate.

The modern frameworks and frontend libraries address speed and help developers ship better user experience to the users. We'll be discussing two of the modern frameworks: Qwik and Next.js. We'll see an experiment benchmark and look into how each framework achieves optimal performance.

Let's go.

How The Experiment Is Set Up

  • qwik v0.16
  • Next.js v13
  • React v18

Let's Peek The Result

I built two identical applications with Qwik and Next.js and measured the performances. The demo look like this:

DALL-E web app demo

You can find the demo on GitHub. Feel free to take a look at the repo and try it out

The application lets users enter prompts to DALL·E, an AI image generator, and displays the generated AI images in the page. It also displays the latest Twitter feed about DALL·E.

The key features are:

Here's the core Web Vitals by Lighthouse for Qwik:

Qwik Lighthouse score

Comparing to Next.js's Web Vitals:

Next with server component Lighthouse score

We can observe the following:

Let's take a closer look at the how the application in each framework is set up.

Qwik Server Side Rendering

The component structure:

qwik/src/routes/index.tsx
export const onGet: RequestHandler<TwitterResponse> = async () => {
  const data = await fetchTweets();
  return data;
}

export default component$(() => {
  const tweets = useEndpoint<TwitterResponse>();
  return (
    <Layout>
      <DallePromptAndResult />
      <Resource
        value={tweets}
        onResolved={(result) => (
          <SSRTwitterCarousel data={result} />
        )}
      />
      <StaticPromptRecommendation />
    </Layout>
  )
})

When the Qwik server receives a page request, it starts the rendering process on the server. The "useEndPoint" function invokes the "onGet" function on the server and fetches the twitter feed. The "Resource" component will pause the rendering until the twitter data is resolved or rejected. Once the rendering is completed, the server responds to the client with rendered HTML.

We can observe the server side rendering behavior in the performance profile:

Qwik performance

The lead time to respond to the client is blocked by the server side data fetching and component rendering.

Next.js SSR and Streaming

The component structure:

next/app/page.tsx
export default async function Page() {
  const tweets = await fetchTweets();
  return (
    <Layout>
      <DallePromptAndResult />
      <Suspense fallback={<Skeleton />}>
        <SSRTwitterCarousel data={tweets} />
      </Suspense>
      <StaticPromptRecommendation />
    </Layout>
  )
}

The "Page" component is a server component. It looks just like a normal component but it supports async/await.

When the Next.js server receives a page request, it starts the rendering process and streaming the rendering result to the client so that the client can progressively render and display the UI to the users. While the server is fetching twitter feeds, React renders the suspense placeholder to indicate the pending state. Once the data is resolved or rejected, React reveals the suspense boundary and displays the final UI.

Next with server component performance

You can see the client starts rendering while the server is streaming. It reduces the lead time for the users to start seeing the page.

Resumability v.s. React Server Component

Qwik embraces the "Get HTML and render" mental model.

Resumability is Qwik's innovation. It allows an application to be rendered as much as possible on the server and resumes the rest of the rendering on the client.

The framework looks like this:

Next with server component performance

When receiving a request, the Qwik server starts the rendering process and generate

  • serialized application state,
  • serialized event handlers,
  • component HTMLs,
  • and component code chunks.

The server then responds to the client with the page HTML and the serialized state.

Next with server component performance

On the client, the browser processes the critical rendering path and displays the UI. Qwik deserializes the state after the page HTML is loaded. The state contains all the local states of each component. The lazy loaded components are able to be dynamically imported independently without knowing the parent's state because they can refer to the deserialized state for their local states when they are rendering on the client.

Qwik also serializes event handlers.

Qwik serialization

In the server-generated HTML, the event handlers are referenced as dynamic JavaScript chunks. When users interact with an interactive element, Qwik uses the reference to dynamically download the chunk from the server and fire the event with the event handler inside the chunk.

Qwik dynamic import

On the other hand, Next.js has a different approach to server side rendering.

Next.js and React server component promotes the "Render while fetching" mental model.

Next.js 13 introduces React server component as an experimental feature. Server component is a special type of component that can only be rendered on the server. Combining with Suspense and Streaming, the framework is able to progressively render interactive UI while avoiding long data requests from blocking the page rendering.

The framework looks like this:

Next.js and React Server Component

When Next.js server receives a request, it delegates React to handle rendering. On the server, React renders the component tree into a UI description in JSON instead of native HTML. The UI description describes the entire component tree. For the client components, React describes them as components for the client to handle with serialized props. For the server components, React renders them to native HTMLs and places them in the UI description.

On the client, React receives and reconciles the UI description in the response stream to progressively render the UI. When React sees a suspense boundary, it renders the suspense placeholder until the pending data is resolved and reveals the suspense boundary.

Because React receives a UI description instead of native HTML on the client, it needs to construct the component tree, render the UI, and attach event handlers on the client. It's known as hydration.

Next.js and React Server Component

Final Thoughts

Qwik's concept of serializing states and event handlers is very innovative. The client is able to render the page with just HTML and minimal JavaScript. It reduces the amount of JavaScript the client needs to download for the initial load and leverages dynamic import to download event handlers and components on the fly. We can clearly observe the benefit from the benchmark and performance profile.

However, the combination of React server component, suspense, and streaming has a profound impact on user experience. Users are able to see and interact with the content of the page without waiting for server side data fetching to complete.

The result of the experiment is not conclusive to me. Both frameworks performed well in the benchmarks. The differences in First Content Paint, Largest Content Paint, and Cumulative Layout Shift are likely a result of different Twitter images.

References

Here you have it! Thanks for reading through🙌 If you find this article useful, please share it to help more people in their engineering journey.

🐦 Feel free to connect with me on twitter!

⏭ Ready for the next article? 👉 Tired of Slow Code Reviews? Read this

Happy coding!

Daw-Chih Liou
Daw-Chih Liou

Daw-Chih is a software engineer, UX advocate, and creator who is dedicated to Web engineering. His background in Human Centered Computing has led him to work with startups and public companies across North America, Asia, and Europe. He is passionate about meeting business trajectory with user journey and utilizing engineering architecture and performance monitoring to provide optimal user experience.