Qwik vs Marko: A Comprehensive Comparison

This website is powered by ItGalaxy.io

In the world of frontend development, Qwik and Marko represent two innovative approaches to building web applications. While Qwik introduces revolutionary resumability and fine-grained reactivity, Marko offers a unique template-first approach with powerful streaming capabilities. Let’s explore their differences and use cases.

Table of Contents

  1. Core Concepts
  2. Reactivity and State Management
  3. Templating and Components
  4. DOM Manipulation
  5. Event Handling
  6. Component Composition
  7. Form Handling
  8. Lifecycle Management
  9. Web App Features
  10. Performance and Bundle Size
  11. Learning Curve
  12. Conclusion

Core Concepts

Qwik and Marko take fundamentally different approaches to building web applications:

  • Qwik introduces resumability and fine-grained reactivity with automatic lazy loading of JavaScript
  • Marko employs a template-first approach with powerful streaming capabilities and compile-time optimizations

Reactivity and State Management

Declare State

Qwik

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

export const Name = component$(() => {
  const name = useSignal("John");

  return <h1>Hello {name.value}</h1>;
});

Marko

<let/name = "John"/>
<h1>Hello ${name}</h1>

Update State

Qwik

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

export const Name = component$(() => {
  const name = useSignal("John");

  useTask$(() => {
    name.value = "Jane";
  });

  return <h1>Hello {name.value}</h1>;
});

Marko

<let/name = "John"/>
<effect() { name = "Jane" }/>
<h1>Hello ${name}</h1>

Computed State

Qwik

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

export const DoubleCount = component$(() => {
  const count = useSignal(10);
  const doubleCount = useComputed$(() => count.value * 2);

  return <div>{doubleCount.value}</div>;
});

Marko

<let/count = 10/>
<const/doubleCount = count * 2/>
<div>${doubleCount}</div>

Templating

Minimal Template

Qwik

export const HelloWorld = () => {
  return <div>Hello World</div>;
};

Marko

<h1>Hello world</h1>

Styling

Qwik

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

export const App = component$(() => {
  useStyles$(`
    .title {
      color: red;
    }
  `);

  return (
    <>
      <h1 class="title">I am red</h1>
      <button style={{ "font-size": "10rem" }}>I am a button</button>
    </>
  );
});

Marko

<h1.title>I am red</h1>
<button style={ fontSize: "10rem" }>I am a button</button>
<button class=scopedButton>I am a style-scoped button</button>

<style>
  .title {
    color: red;
  }
</style>
<style/{ scopedButton }>
  .scopedButton {
    font-size: 10rem;
  }
</style>

Loop

Qwik

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

export const Colors = component$(() => {
  const colors = ["red", "green", "blue"];
  return (
    <ul>
      {colors.map((color) => (
        <li key={color}>{color}</li>
      ))}
    </ul>
  );
});

Marko

<ul>
  <for|color| of=["red", "green", "blue"]>
    <li>${color}</li>
  </for>
</ul>

Event Click

Qwik

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

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

  const incrementCount = $(() => {
    count.value++;
  });

  return (
    <>
      <p>Counter: {count.value}</p>
      <button onClick$={incrementCount}>Increment</button>
    </>
  );
});

Marko

<let/count = 0/>
<p>Counter: ${count}</p>
<button onClick() { count++ }>+1</button>

DOM Reference

Qwik

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

export const InputFocused = component$(() => {
  const inputElement = useSignal<HTMLInputElement>();

  useVisibleTask$(({ track }) => {
    const el = track(inputElement);
    el?.focus();
  });

  return <input type="text" ref={inputElement} />;
});

Marko

<input/inputElement>
<effect() { inputElement().focus() }/>

Conditional

Qwik

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

export const TRAFFIC_LIGHTS = ["red", "orange", "green"];

export const TrafficLight = component$(() => {
  const lightIndex = useSignal(0);

  const light = useComputed$(() => TRAFFIC_LIGHTS[lightIndex.value]);

  const nextLight = $(() => {
    lightIndex.value = (lightIndex.value + 1) % TRAFFIC_LIGHTS.length;
  });

  return (
    <>
      <button onClick$={nextLight}>Next light</button>
      <p>Light is: {light.value}</p>
      <p>
        You must {light.value === "red" && <span>STOP</span>}
        {light.value === "orange" && <span>SLOW DOWN</span>}
        {light.value === "green" && <span>GO</span>}
      </p>
    </>
  );
});

Marko

static const TRAFFIC_LIGHTS = ["red", "orange", "green"];
<let/lightIndex = 0/>
<const/light = TRAFFIC_LIGHTS[lightIndex]/>

<button onClick() { lightIndex = (lightIndex + 1) % TRAFFIC_LIGHTS.length }>
  Next light
</button>
<p>Light is: ${light}</p>
<p>
  You must
  <if=light === "red">STOP</if>
  <else-if=light === "orange">SLOW DOWN</else-if>
  <else>GO</else>
</p>

Lifecycle

On Mount

Qwik

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

export const App = component$(() => {
  const store = useStore({
    pageTitle: "",
  });

  useVisibleTask$(() => {
    store.pageTitle = document.title;
  });

  return <p>Page title: {store.pageTitle}</p>;
});

Marko

<let/pageTitle = ""/>
<effect() { pageTitle = document.title }/>
<p>Page title: ${pageTitle}</p>

On Unmount

Qwik

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

export const App = component$(() => {
  const store = useStore({
    time: new Date().toLocaleTimeString(),
  });

  useVisibleTask$(({ cleanup }) => {
    const timer = setInterval(() => {
      store.time = new Date().toLocaleTimeString();
    }, 1000);

    cleanup(() => clearInterval(timer));
  });

  return <p>Current time: {store.time}</p>;
});

Marko

<let/time = new Date()/>
<lifecycle
  onMount() { this.timer = setInterval(_ => time = new Date(), 1000) }
  onDestroy() { clearInterval(this.timer) }
/>
<p>Current time: ${time.toLocaleTimeString()}</p>

Component Composition

Props

Qwik

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

const App = component$(() => {
  return (
    <UserProfile
      name="John"
      age={20}
      favouriteColors={["green", "blue", "red"]}
      isAvailable
    />
  );
});

export default App;

Marko

<UserProfile
  name="John"
  age=20
  favouriteColors=["green", "blue", "red"]
  isAvailable
/>

Emit to Parent

Qwik

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

const App = component$(() => {
  const store = useStore({
    isHappy: true,
  });

  const onAnswerNo = $(() => {
    store.isHappy = false;
  });

  const onAnswerYes = $(() => {
    store.isHappy = true;
  });

  return (
    <>
      <p>Are you happy?</p>
      <AnswerButton onYes$={onAnswerYes} onNo$={onAnswerNo} />
      <p style={{ fontSize: 50 }}>{store.isHappy ? "😀" : "😥"}</p>
    </>
  );
});

export default App;

Marko

<let/isHappy = true/>
<p>Are you happy?</p>
<AnswerButton
  onYes() { isHappy = true }
  onNo() { isHappy = false }
/>
<p style={ fontSize: 50 }>${isHappy ? "😀" : "😥"}</p>

Slot

Qwik

import FunnyButton from "./FunnyButton";

export default function App() {
  return <FunnyButton>Click me!</FunnyButton>;
}

Marko

<FunnyButton>Click me!</FunnyButton>

Slot Fallback

Qwik

import FunnyButton from "./FunnyButton";

export default function App() {
  return (
    <>
      <FunnyButton />
      <FunnyButton>Click me!</FunnyButton>
    </>
  );
}

Marko

<FunnyButton/>
<FunnyButton>I got content!</FunnyButton>

Context

Qwik

import {
  component$,
  useStore,
  useContextProvider,
  createContext,
  $,
} from "@builder.io/qwik";
import UserProfile from "./UserProfile";

export const UserContext = createContext("user-context");

const App = component$(() => {
  const user = useStore({
    id: 1,
    username: "unicorn42",
    email: "unicorn42@example.com",
  });

  const updateUsername = $((newUsername) => {
    user.username = newUsername;
  });

  useContextProvider(UserContext, { user, updateUsername });

  return (
    <>
      <h1>Welcome back, {user.username}</h1>
      <UserProfile />
    </>
  );
});

export default App;

Marko

<let/user = {
  id: 1,
  username: "unicorn42",
  email: "unicorn42@example.com",
}/>
<const/updateUsername(newUsername) {
  user = { ...user, username: newUsername };
}/>

<h1>Welcome back, ${user.username}</h1>
<set={ ...user, updateUsername }>
  <UserProfile />
</set>

Form Input

Input Text

Qwik

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

const InputHello = component$(() => {
  const text = useSignal("");

  return (
    <>
      <p>{text.value}</p>
      <input bind:value={text} />
    </>
  );
});

export default InputHello;

Marko

<let/text = "Hello world"/>
<p>${text}</p>
<input value:=text/>

Checkbox

Qwik

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

const IsAvailable = component$(() => {
  const isAvailable = useSignal(false);

  return (
    <>
      <input id="is-available" type="checkbox" bind:checked={isAvailable} />
      <label for="is-available">Is available</label>
    </>
  );
});

export default IsAvailable;

Marko

<input#is-available
  type="checkbox"
  checked:=input.isAvailable
/>
<label for="is-available">Is available</label>

Radio

Qwik

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

const PickPill = component$(() => {
  const pickedColor = useSignal("red");

  return (
    <>
      <div>Picked: {pickedColor.value}</div>
      <input
        id="blue-pill"
        type="radio"
        bind:value={pickedColor}
        checked={pickedColor.value === "blue"}
        value="blue"
      />
      <label for="blue-pill">Blue pill</label>

      <input
        id="red-pill"
        type="radio"
        checked={pickedColor.value === "red"}
        bind:value={pickedColor}
        value="red"
      />
      <label for="red-pill">Red pill</label>
    </>
  );
});

export default PickPill;

Marko

<let/picked = "red"/>
<const/handleChange(event) {
  picked = event.target.value;
}/>

<div>Picked: ${picked}</div>
<input#blue-pill
  type="radio"
  checked=picked === "blue"
  value="blue"
  onChange=handleChange
/>
<label for="blue-pill">Blue pill</label>

<input#red-pill
  type="radio"
  checked=picked === "red"
  value="red"
  onChange=handleChange
/>
<label for="red-pill">Red pill</label>

Select

Qwik

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

export const colors = [
  { id: 1, text: "red" },
  { id: 2, text: "blue" },
  { id: 3, text: "green" },
  { id: 4, text: "gray", isDisabled: true },
];

const ColorSelect = component$(() => {
  const selectedColorId = useSignal("2");

  return (
    <select bind:value={selectedColorId}>
      {colors.map((color) => (
        <option
          key={color.id}
          value={color.id}
          disabled={color.isDisabled}
          selected={`${color.id}` === selectedColorId.value}
        >
          {color.text}
        </option>
      ))}
    </select>
  );
});

export default ColorSelect;

Marko

static const colors = [
  { id: 1, text: "red" },
  { id: 2, text: "blue" },
  { id: 3, text: "green" },
  { id: 4, text: "gray", isDisabled: true },
];
<let/selectedColorId = 2/>

<select onChange(event) { selectedColorId = event.target.value }>
  <for|{ id, isDisabled, text }| of=colors>
    <option value=id disabled=isDisabled selected=id === selectedColorId>
      ${text}
    </option>
  </for>
</select>

Web App Features

Render App

Qwik

<!DOCTYPE html>
<html>
  <body>
    <div id="app"></div>
    <script type="module" src="./main.tsx"></script>
  </body>
</html>

Marko

<!DOCTYPE html>
<html>
  <App/>
</html>

Fetch Data

Qwik

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

type UsersResponse = {
  results: {
    picture: {
      thumbnail: string;
    };
    name: {
      first: string;
      last: string;
    };
  }[];
};

export async function fetchUsers() {
  return (await fetch("https://randomuser.me/api/?results=3")).json();
}

export const App = component$(() => {
  const data = useResource$<UsersResponse>(fetchUsers);

  return (
    <Resource
      value={data}
      onPending={() => <p>Fetching users...</p>}
      onRejected={() => <p>An error occurred while fetching users</p>}
      onResolved={({ results: users }) => (
        <ul>
          {users.map((user) => (
            <li>
              <img src={user.picture.thumbnail} alt="user" />
              <p>
                {user.name.first} {user.name.last}
              </p>
            </li>
          ))}
        </ul>
      )}
    />
  );
});

Marko

<await(fetch("https://randomuser.me/api/?results=3").then(res => res.json()))>
  <@placeholder>
    <p>Fetching users...</p>
  </@placeholder>
  <@catch|error|>
    <p>An error occurred while fetching users</p>
  </@catch>
  <@then|{ results: users }|>
    <ul>
      <for|{ picture, name }| of=users>
        <li>
          <img src=picture.thumbnail alt="user">
          <p>${name.first} ${name.last}</p>
        </li>
      </for>
    </ul>
  </@then>
</await>

Performance and Bundle Size

Qwik

  • Zero hydration
  • Resumable applications
  • Fine-grained lazy loading
  • Optimal initial page load
  • Progressive JavaScript loading

Marko

  • Compile-time optimizations
  • Streaming rendering
  • Partial hydration
  • Automatic code splitting
  • Small runtime footprint

Learning Curve

Qwik

  • New concepts (resumability, lazy loading)
  • JSX-like syntax familiar to React developers
  • Strong TypeScript support
  • Growing documentation and community
  • Innovative development patterns

Marko

  • Template-first approach
  • Unique syntax to learn
  • Built-in streaming capabilities
  • Growing documentation and community
  • Innovative development patterns

Conclusion

Choose Qwik if you:

  • Need optimal initial page load
  • Want zero hydration overhead
  • Need progressive JavaScript loading
  • Value modern development features
  • Are building large, scalable applications

Choose Marko if you:

  • Need streaming server rendering
  • Want compile-time optimizations
  • Need partial hydration
  • Value template-first development
  • Are building large, scalable applications

Both frameworks excel in different scenarios:

  • Qwik is perfect for applications that need optimal loading and modern features
  • Marko shines in applications that need streaming capabilities and compile-time optimizations

The choice between Qwik and Marko often depends on your specific needs:

  • Use Qwik for applications that need optimal loading and modern development features
  • Use Marko for applications that need streaming capabilities and compile-time optimizations






Decouvrez plus d’Offres de la plateform ItGalaxy.io :

Découvrez notre gamme complète de services et formations pour accélérer votre carrière.

1. Nous contactez

  • Description: Besoin de Formation et des Solutions cloud complètes pour vos applications
  • Links:

2. Infra as a Service

  • Description: Infrastructure cloud évolutive et sécurisée
  • Links:

3. Projets Développeurs


4. Développeurs


5. Formations Complètes


6. Marketplace

7. Blogs


This website is powered by ItGalaxy.io