Skip to main content
On this page

Build Qwik with Deno

Qwik is a JavaScript framework that delivers instant-loading web applications by leveraging resumability instead of hydration. In this tutorial, we'll build a simple Qwik application and run it with Deno. The app will display a list of dinosaurs. When you click on one, it'll take you to a dinosaur page with more details.

We'll go over how to build a simple Qwik app using Deno:

Feel free to skip directly to the source code or follow along below!

Scaffold a Qwik app Jump to heading

We can create a new Qwik project using deno like this:

deno init --npm qwik@latest

This will run you through the setup process for Qwik and Qwik City. Here, we chose the simplest “Empty App” deployment with npm dependencies.

When complete, you’ll have a project structure that looks like this:

.
├── node_modules/
├── public/
└── src/
    ├── components/
    │   └── router-head/
    │       └── router-head.tsx
    └── routes/
        ├── index.tsx
        ├── layout.tsx
        ├── service-worker.ts
        ├── entry.dev.tsx
        ├── entry.preview.tsx
        ├── entry.ssr.tsx
        ├── global.css
        └── root.tsx
├── .eslintignore
├── .eslintrc.cjs
├── .gitignore
├── .prettierignore
├── package-lock.json
├── package.json
├── qwik.env.d.ts
├── README.md
├── tsconfig.json
└── vite.config.ts

Most of this is boilerplate configuration that we won’t touch. A few of the important files to know for how Qwik works are:

  • src/components/router-head/router-head.tsx: Manages the HTML head elements (like title, meta tags, etc.) across different routes in your Qwik application.
  • src/routes/index.tsx: The main entry point and home page of your application that users see when they visit the root URL.
  • src/routes/layout.tsx: Defines the common layout structure that wraps around pages, allowing you to maintain consistent UI elements like headers and footers.
  • src/routes/service-worker.ts: Handles Progressive Web App (PWA) functionality, offline caching, and background tasks for your application.
  • src/routes/entry.ssr.tsx: Controls how your application is server-side rendered, managing the initial HTML generation and hydration process.
  • src/routes/root.tsx: The root component that serves as the application's shell, containing global providers and the main routing structure.

Now we can build out our own routes and files within the application.

Setup data and type definitions Jump to heading

We’ll start by adding our dinosaur data to a new ./src/data directory as dinosaurs.json:

// ./src/data/dinosaurs.json

{
  "dinosaurs": [
    {
      "name": "Tyrannosaurus Rex",
      "description": "A massive carnivorous dinosaur with powerful jaws and tiny arms."
    },
    {
      "name": "Brachiosaurus",
      "description": "A huge herbivorous dinosaur with a very long neck."
    },
    {
      "name": "Velociraptor",
      "description": "A small but fierce predator that hunted in packs."
    }
    // ...
  ]
}

This is where our data will be pulled from. In a full application, this data would come from a database.

⚠️️ In this tutorial we hard code the data. But you can connect to a variety of databases and even use ORMs like Prisma with Deno.

Next, let's add type definitions for our dinosaur data. We'll put it in types.ts in ./src/:

// ./src/types.ts

export type Dino = {
  name: string;
  description: string;
};

Next, let's add API routes to server this data.

Add API routes Jump to heading

First, let's create the route to load all dinosaurs for the index page. This API endpoint uses Qwik City's RequestHandler to create a GET endpoint that loads and returns our dinosaur data using the json helper for proper response formatting. We'll add the below to a new file in ./src/routes/api/dinosaurs/index.ts:

// ./src/routes/api/dinosaurs/index.ts

import { RequestHandler } from "@builder.io/qwik-city";
import data from "~/data/dinosaurs.json" with { type: "json" };

export const onGet: RequestHandler = async ({ json }) => {
  const dinosaurs = data;
  json(200, dinosaurs);
};

Next, let's create the API route to get the information for a single dinosaur. This takes the parameter from the URL and uses it to search through our dinosaur data. We'll add the below code to ./src/routes/api/dinosaurs/[name]/index.ts:

// ./src/routes/api/dinosaurs/[name]/index.ts

import { RequestHandler } from "@builder.io/qwik-city";
import data from "~/data/dinosaurs.json" with { type: "json" };

export const onGet: RequestHandler = async ({ params, json }) => {
  const { name } = params;
  const dinosaurs = data;

  if (!name) {
    json(400, { error: "No dinosaur name provided." });
    return;
  }

  const dinosaur = dinosaurs.find(
    (dino) => dino.name.toLowerCase() === name.toLowerCase(),
  );

  if (!dinosaur) {
    json(404, { error: "No dinosaur found." });
    return;
  }

  json(200, dinosaur);
};

Now that the API routes are wired up and serving data, let's create the two frontend pages: the index page and the individual dinosaur detail pages.

Build the frontend Jump to heading

We'll create our homepage by updating our ./src/routes/index.tsx file using Qwik's routeLoader$ for server-side data fetching. This component$ loads and renders the dinosaur data during SSR via useDinosaurs():

// ./src/routes/index.tsx

import { component$ } from "@builder.io/qwik";
import { Link, routeLoader$ } from "@builder.io/qwik-city";
import type { Dino } from "~/types";
import data from "~/data/dinosaurs.json" with { type: "json" };

export const useDinosaurs = routeLoader$(() => {
  return data;
});

export default component$(() => {
  const dinosaursSignal = useDinosaurs();

  return (
    <div class="container mx-auto p-4">
      <h1 class="text-3xl font-bold mb-4">Welcome to the Dinosaur app</h1>
      <p class="mb-4">Click on a dinosaur below to learn more.</p>
      <ul class="space-y-2">
        {dinosaursSignal.value.map((dinosaur: Dino) => (
          <li key={dinosaur.name}>
            <Link
              href={`/${dinosaur.name.toLowerCase()}`}
              class="text-blue-600 hover:underline"
            >
              {dinosaur.name}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
});

Now that we have our main index page, let's add a page for the individual dinosaur information. We'll use Qwik's dynamic routing, with [name] as the key for each dinosaur. This page leverages routeLoader$ to fetch individual dinosaur details based on the URL parameter, with built-in error handling if the dinosaur isn't found.

The component uses the same SSR pattern as our index page, but with parameter-based data loading and a simpler display layout for individual dinosaur details:

// ./src/routes/[name]/index.tsx

import { component$ } from "@builder.io/qwik";
import { Link, routeLoader$ } from "@builder.io/qwik-city";
import type { Dino } from "~/types";
import data from "~/data/dinosaurs.json" with { type: "json" };

export const useDinosaurDetails = routeLoader$(({ params }): Dino => {
  const dinosaurs = data;
  const dinosaur = dinosaurs.find(
    (dino: Dino) => dino.name.toLowerCase() === params.name.toLowerCase(),
  );

  if (!dinosaur) {
    throw new Error("Dinosaur not found");
  }

  return dinosaur;
});

export default component$(() => {
  const dinosaurSignal = useDinosaurDetails();

  return (
    <div class="container mx-auto p-4">
      <h1 class="text-3xl font-bold mb-4">{dinosaurSignal.value.name}</h1>
      <p class="mb-4">{dinosaurSignal.value.description}</p>
      <Link href="/" class="text-blue-600 hover:underline">
        Back to all dinosaurs
      </Link>
    </div>
  );
});

Now that we have built our routes and the frontend components, we can run our application:

deno task dev

This will start the app at localhost:5173:

Tada!

Next steps Jump to heading

🦕 Now you can build and run a Qwik app with Deno! Here are some ways you could enhance your dinosaur application:

Next steps for a Qwik app might be to use Qwik's lazy loading capabilities for dinosaur images and other components, or add client-side state management for complex features.

Did you find what you needed?

Privacy policy