← Teacher guide

Same app, three frameworks

Students predict that three versions of the same app (Vue, Preact, Svelte) behave alike, sort each piece of code by the idea it is, find React's update catch in Preact, add an if in Svelte and a Clear button in Preact, and add one feature to all three.

Apps with Vue · Final challenge: a to-do app · about 50 minutes · Open the student lesson

Classroom resources

Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.

Students will be able to

Timing

Predict and Run 10 min · Investigate 15 min · Modify 10 min · Make 15 min

Before class

An optional lesson for students who finished the to-do app. The point: components, state, derived (worked-out) values and events are the same ideas in every framework; only the syntax and the update rules differ.

Every library is served from this site, not a CDN.

Running the lesson

  1. Predict: ask students who did the computed lesson whether Preact's plain count will go stale.
  2. Run: point out the short wait while Svelte compiles.
  3. Investigate: do the sort as a class, one framework at a time.
  4. Modify: ask what each framework calls v-if, and what Preact needs instead of changing the list.
  5. Make: ask which version they would rather read in a year's time, and why.

The hard part (on purpose)

The three spellings blur together. Keep returning to the four ideas: ask "which line is the data? the worked-out value? the event?" in each panel.

When students get stuck

What you will seeA question to askThe fix
Preact's page does not change.Does setStars get a new list, or the old one changed?setStars([...stars, "⭐"]) or stars.slice(0, -1): always a new list.
Svelte shows a red error about reading the component.Which line of the component does the message name?Check the braces { } and the {#if} … {/if} pair on that line.
The Svelte app does not appear at all.Is svelte.js loaded after the textarea, and does the textarea have data-svelte?Keep the textarea with data-svelte hidden, and the script src="svelte.js" line after it.

Answers to the checks

  1. Predict: You click Add a star twice in each app. What do they show?
    All three show Stars: 2. Preact runs its whole App function again after each change, so its plain count line runs again too.
  2. Sort the ideas: Sort each piece of code by the idea it is.
    Vue: ref([]) → Data that changes; Preact: useState([]) → Data that changes; Svelte: $state([]) → Data that changes; Vue: computed(() => stars.value.length) → A worked-out value; Preact: const count = stars.length → A worked-out value; Svelte: $derived(stars.length) → A worked-out value; Vue: @click="add" → An event; Preact: onClick=${add} → An event; Svelte: onclick={add} → An event; Vue: {{ count }} → Showing data; Preact: ${count} → Showing data; Svelte: {count} → Showing data
    Four ideas, three ways to write each. Learn the ideas once; each framework is new spelling.
  3. Why no computed?: In Vue, a total worked out with a plain line went stale. Why is Preact's plain line, const count = stars.length, always right?
    Preact runs the whole App function again after every change, so the line runs again
    Vue runs setup once and watches refs; Preact (and React) run the component function again each time. Different update rules, the same result on the page.
  4. Spot the bug: In this Preact app, Add a star does nothing: the count stays at 0. There is no error. Click the two lines that are wrong.
    line 8 and 9
    push changes the old list, then setStars is given that same list, so Preact sees nothing new and skips the update. Give it a new list: setStars([...stars, "⭐"]).
  5. Where this leads: A school website team asks which framework to learn. What is the best advice?
    Learn the ideas (state, worked-out values, events, components) well in one; the others are new spelling
    The ideas outlast any one framework. Real projects add a build tool (npm create vue, Vite) and one file per component; your ideas carry straight over.

Solutions

One possible solution for each task. Other correct programs are fine.

Modify › An if in Svelte

<h2>Svelte</h2>
<textarea data-svelte hidden>
  <script>
    let stars = $state([]);
    let count = $derived(stars.length);
    function add() {
      stars.push("⭐");
    }
  </script>
  <button onclick={add}>Add a star</button>
  <p>Stars: {count} {stars.join("")}</p>
  {#if count >= 5}
    <p>That's a lot of stars!</p>
  {/if}
</textarea>
<script src="svelte.js"></script>

Modify › Clear in Preact

<h2>Preact</h2>
<div id="preact-app"></div>
<script src="preact.js"></script>
<script>
  const { html, render, useState } = htmPreact;
  function App() {
    const [stars, setStars] = useState([]);
    const count = stars.length;
    function add() {
      setStars([...stars, "⭐"]);
    }
    function clear() {
      setStars([]);
    }
    return html`
      <button onClick=${add}>Add a star</button>
      <button onClick=${clear}>Clear</button>
      <p>Stars: ${count} ${stars.join("")}</p>`;
  }
  render(html`<${App} />`, document.querySelector("#preact-app"));
</script>

the same feature three times

<h2>Vue</h2>
<div id="vue-app">
  <button @click="add">Add a star</button>
  <button @click="remove">Remove one</button>
  <p>Stars: {{ count }} {{ stars.join("") }}</p>
</div>
<script src="vue.js"></script>
<script>
  const { createApp, ref, computed } = Vue;
  createApp({
    setup() {
      const stars = ref([]);
      const count = computed(() => stars.value.length);
      function add() {
        stars.value.push("⭐");
      }
      function remove() {
        stars.value.pop();
      }
      return { stars, count, add, remove };
    }
  }).mount("#vue-app");
</script>

<h2>Preact</h2>
<div id="preact-app"></div>
<script src="preact.js"></script>
<script>
  const { html, render, useState } = htmPreact;
  function App() {
    const [stars, setStars] = useState([]);
    const count = stars.length;
    function add() {
      setStars([...stars, "⭐"]);
    }
    function remove() {
      setStars(stars.slice(0, -1));
    }
    return html`
      <button onClick=${add}>Add a star</button>
      <button onClick=${remove}>Remove one</button>
      <p>Stars: ${count} ${stars.join("")}</p>`;
  }
  render(html`<${App} />`, document.querySelector("#preact-app"));
</script>

<h2>Svelte</h2>
<textarea data-svelte hidden>
  <script>
    let stars = $state([]);
    let count = $derived(stars.length);
    function add() {
      stars.push("⭐");
    }
    function remove() {
      stars.pop();
    }
  </script>
  <button onclick={add}>Add a star</button>
  <button onclick={remove}>Remove one</button>
  <p>Stars: {count} {stars.join("")}</p>
</textarea>
<script src="svelte.js"></script>

Stretch and support

Stretch: Make each framework's star list into a component of its own. Find the same app in React on react.dev and compare it with the Preact version.

Support: Do the sort together on the board; students do only the Svelte if.

Exit ticket

Name one idea that all three frameworks share, and one thing that differs.

Shared: data that changes and a page that follows it (or worked-out values, events, components). Different: the spelling, or the update rule (Preact replaces data, Vue and Svelte can change it).

What each step is for

Where it leads

Real projects: npm create vue@latest (Vue), Vite with React, or npx sv create (Svelte), each with one file per component. Class discussion: why might a team choose each one (popularity and jobs, simplicity, speed)?

Sources