---
title: Getting Started | Introduction
contributors:
  - manucorporat
  - jesperp
  - adamdbradley
  - steve8708
  - cunzaizhuyi
  - mousaAM
  - zanettin
  - Craiqser
  - MyltsinVV
  - literalpie
  - colynyu
  - saisrikardumpeti
  - ahmadalfy
  - renomureza
  - mhevery
  - AnthonyPAlicea
  - kapunahelewong
  - kushalmahajan
  - sreeisalso
  - dustinsgoodman
  - nsdonato
  - seqshem
  - ryo-manba
  - EamonHeffernan
  - DKozachenko
  - mrhoodz
  - moinulmoin
  - lanc3lo1
  - johnrackles
  - kushalvmahajan
  - daniela-bonvini
  - jemsco
  - maiieul
updated_at: '2024-15-30T18:34:34Z'
created_at: '2023-04-02T16:36:48Z'
---

import CodeSandbox from '../../../../components/code-sandbox/index.tsx';
import PackageManagerTabs from '~/components/package-manager-tabs/index.tsx';
import { Note, LongNote } from '~/components/note/note';

# Getting Started Qwikly

Qwik is a new kind of framework that is [resumable](../concepts/resumable/index.mdx) (no eager JS execution and no hydration), built for the edge and [familiar to React developers](/docs/integrations/react/index.mdx).

To play with it right away, check out Qwik's in-browser playgrounds:

- [StackBlitz Qwik](https://qwik.new) (Full Qwik + Qwik Router integration)
- [Examples playground](/examples/reactivity/counter/) (Qwik only, no routing)

## Prerequisites

To get started with Qwik locally, you need the following:

- [Node.js v18.17](https://nodejs.org/en/download/) or higher
- Your favorite IDE ([vscode](https://code.visualstudio.com/) recommended)
- Optionally, read [think qwik](../concepts/think-qwik/index.mdx)

## Create an app using the CLI

First, use the Qwik CLI to generate a blank starter application, to quickly familiarize yourself with it.

Run the Qwik CLI in your shell. Qwik supports pnpm, npm, yarn and bun. Choose the package manager you prefer and run one of the following commands:

<PackageManagerTabs>
<span q:slot="pnpm">
```shell
pnpm create qwik@latest
```
</span>
<span q:slot="npm">
```shell
npm create qwik@latest
```
</span>
<span q:slot="yarn">
```shell
yarn create qwik
```
</span>
<span q:slot="bun">
```shell
bun create qwik@latest
```
</span>
</PackageManagerTabs>

The CLI guides you through an interactive menu to set the project-name, select one of the starters, and ask if you want to install the dependencies.  To find out more about the files generated, refer to the [Project Structure](/docs/project-structure/) documentation.

Start the development server:

<PackageManagerTabs>
<span q:slot="pnpm">
```shell
pnpm run start
```
</span>
<span q:slot="npm">
```shell
npm run start
```
</span>
<span q:slot="yarn">
```shell
yarn run start
```
</span>
<span q:slot="bun">
```shell
bun run start
```
</span>
</PackageManagerTabs>

## Qwik Joke App

The Qwik Hello World tutorial guides you through building a joke app with Qwik while covering the most important Qwik concepts. The app displays a random joke from https://icanhazdadjoke.com and features a button to get a new joke on click.


### 1. Create A Route

Start by serving a page at a particular route. This basic app serves a random dad joke application on the `/joke/` route. This tutorial relies on Qwik Router, which uses [directory-based](/docs/routing/) routing. To get started:

1. In your project, create a new `joke` directory in `routes` containing an `index.tsx` file.
2. Each route's `index.tsx` file must have an `export default component$(...)` so that Qwik Router knows what content to serve. Paste the following content to `src/routes/joke/index.tsx`:

<CodeSandbox src="/src/routes/demo/getting-started/01-route/index.tsx">
```tsx {3-5} title="src/routes/joke/index.tsx"
import { component$ } from '@qwik.dev/core';

export default component$(() => {
  return <section class="section bright">A Joke!</section>;
});
```
</CodeSandbox>

3. Navigate to `localhost:5173/joke/` to see your new page working.

<LongNote title="Route and Component Basics">
- Your `joke` route default component is surrounded by an existing layout. See [Layout](/docs/layout/) for more details on what layouts are and how to work with them.
- index.tsx, layout.tsx in routes folder, root.tsx and all entry files need **export default**. For other components you can use export const and export function.
- For more details about how to author components, see the [Component API](/docs/(qwik)/core/overview/index.mdx) section.
</LongNote>

### 2. Loading Data

Use the external JSON API at https://icanhazdadjoke.com to load random jokes. You can use [route loaders](/docs/route-loader/) to load this data into the server and render it in the component.

1. Open `src/routes/joke/index.tsx` and add this code:

<CodeSandbox src="/src/routes/demo/getting-started/02-loading-data/index.tsx">
```tsx /routeLoader$/ /useDadJoke/#a title="src/routes/joke/index.tsx"
import { component$ } from '@qwik.dev/core';
import { routeLoader$ } from '@qwik.dev/router';

export const useDadJoke = routeLoader$(async () => {
  const response = await fetch('https://icanhazdadjoke.com/', {
    headers: { Accept: 'application/json' },
  });
  return (await response.json()) as {
    id: string;
    status: number;
    joke: string;
  };
});

export default component$(() => {
  // Calling our `useDadJoke` hook, will return a reactive signal to the loaded data.
  const dadJokeSignal = useDadJoke();
  return (
    <section class="section bright">
      <p>{dadJokeSignal.value.joke}</p>
    </section>
  );
});
```
</CodeSandbox>

2. Now on `http://localhost:5173/joke/`, the browser displays a random joke.

Code explanation:

- The function passed to `routeLoader$` is invoked on the server eagerly before any component is rendered and is responsible for loading data.
- The `routeLoader$` returns a use-hook, `useDadJoke()`, that can be used in the component to retrieve the server data.

<LongNote title="routeLoader$ Behavior">
- The `routeLoader$` is invoked eagerly on the server before any component is rendered, even if its use-hook is not invoked in any component.
- The `routeLoader$` return type is inferred in the component without the need for any additional type information.
</LongNote>


### 3. Posting Data to the Server

Previously, the component `routeLoader$` was used to send data from the server to the client. To post (send) data from the client back to the server, use [`routeAction$`](/docs/action/).

NOTE: `routeAction$` is the preferred way to send data to the server because it uses the browser native form API, which works even if JavaScript is disabled.

To declare an action, add this code:

```tsx /routeAction\$/ title="src/routes/joke/index.tsx"
import { routeLoader$, Form, routeAction$ } from '@qwik.dev/router';

export const useJokeVoteAction = routeAction$(
  (props) => {
    // Leave it as an exercise for the reader to implement this.
    console.log('VOTE', props);
  },
  { invalidate: [useDadJoke] }
);
```

2. Update the `export default` component to use the `useJokeVoteAction` hook with `<Form>`.

```tsx {3,7-11} /favoriteJokeAction/ title="src/routes/joke/index.tsx"
export default component$(() => {
  const dadJokeSignal = useDadJoke();
  const favoriteJokeAction = useJokeVoteAction();
  return (
    <section class="section bright">
      <p>{dadJokeSignal.value.joke}</p>
      <Form action={favoriteJokeAction}>
        <input type="hidden" name="jokeID" value={dadJokeSignal.value.id} />
        <button name="vote" value="up">👍</button>
        <button name="vote" value="down">👎</button>
      </Form>
    </section>
  );
});
```

3. Now the buttons display on `http://localhost:5173/joke/`, and their values will log to the console when they are clicked.

Code explanation:

- `routeAction$` receives the data.
  - The function passed to `routeAction$` is invoked on the server whenever the form is posted.
  - The `invalidate` option tells Qwik to re-run `useDadJoke` after the action completes.
  - The `routeAction$` returns a use-hook, `useJokeVoteAction`, that you can use in the component to post the form data.
- `Form` is a convenience component that wraps the browser's native `<form>` element.

Things to note:

- For validation, see [zod validation](/docs/action/#validation-and-type-safety).
- The `routeAction$` works even if JavaScript is disabled.
- If JavaScript is enabled, the `Form` component will prevent the browser from posting the form and instead post the data using JavaScript and emulate the browser's native form behavior without a full refresh.

For reference, the complete code snippet for this section is as follows:

<CodeSandbox src="/src/routes/demo/getting-started/03-posting-data/index.tsx" style={{ height: '8em' }}>
```tsx /favoriteJokeAction/ /invalidate/ title="src/routes/joke/index.tsx"
import { component$ } from '@qwik.dev/core';
import { routeLoader$, Form, routeAction$ } from '@qwik.dev/router';

export const useDadJoke = routeLoader$(async () => {
  const response = await fetch('https://icanhazdadjoke.com/', {
    headers: { Accept: 'application/json' },
  });
  return (await response.json()) as {
    id: string;
    status: number;
    joke: string;
  };
});

export const useJokeVoteAction = routeAction$(
  (props) => {
    console.log('VOTE', props);
  },
  { invalidate: [useDadJoke] }
);

export default component$(() => {
  // Calling our `useDadJoke` hook, will return a reactive signal to the loaded data.
  const dadJokeSignal = useDadJoke();
  const favoriteJokeAction = useJokeVoteAction();
  return (
    <section class="section bright">
      <p>{dadJokeSignal.value.joke}</p>
      <Form action={favoriteJokeAction}>
        <input type="hidden" name="jokeID" value={dadJokeSignal.value.id} />
        <button name="vote" value="up">
          👍
        </button>
        <button name="vote" value="down">
          👎
        </button>
      </Form>
    </section>
  );
});
```
</CodeSandbox>

### 4. Modifying State

Keeping track of the state and updating the UI is core to what applications do. Qwik provides a `useSignal` hook to keep track of the application's state. To learn more, see [state management](/docs/core/state/).

To declare state:

1. Import `useSignal` from `qwik`.
   ```tsx /useSignal/
   import { component$, useSignal } from "@qwik.dev/core";
   ```
2. Declare the component's state using `useSignal()`.
   ```tsx /useSignal/
   const isFavoriteSignal = useSignal(false);
   ```
3. After the closing `Form` tag, add a button to the component to modify the state.
   ```tsx /isFavoriteSignal/
   <button
    onClick$={() => {
      isFavoriteSignal.value = !isFavoriteSignal.value;
    }}>
     {isFavoriteSignal.value ? '❤️' : '🤍'}
   </button>
   ```

NOTE: Clicking on the button updates the state, which in turn updates the UI.

For reference, the complete code snippet for this section is as follows:

<CodeSandbox src="/src/routes/demo/getting-started/04-state/index.tsx" style={{ height: '10em' }} maxHeight={500}>
```tsx /isFavoriteSignal/ title="src/routes/joke/index.tsx"
import { component$, useSignal } from '@qwik.dev/core';
import { routeLoader$, Form, routeAction$ } from '@qwik.dev/router';

export const useDadJoke = routeLoader$(async () => {
  const response = await fetch('https://icanhazdadjoke.com/', {
    headers: { Accept: 'application/json' },
  });
  return (await response.json()) as {
    id: string;
    status: number;
    joke: string;
  };
});

export const useJokeVoteAction = routeAction$(
  (props) => {
    console.log('VOTE', props);
  },
  { invalidate: [useDadJoke] }
);

export default component$(() => {
  const isFavoriteSignal = useSignal(false);
  // Calling our `useDadJoke` hook, will return a reactive signal to the loaded data.
  const dadJokeSignal = useDadJoke();
  const favoriteJokeAction = useJokeVoteAction();

  return (
    <section class="section bright">
      <p>{dadJokeSignal.value.joke}</p>
      <Form action={favoriteJokeAction}>
        <input type="hidden" name="jokeID" value={dadJokeSignal.value.id} />
        <button name="vote" value="up">
          👍
        </button>
        <button name="vote" value="down">
          👎
        </button>
      </Form>
      <button
        onClick$={() => (isFavoriteSignal.value = !isFavoriteSignal.value)}
      >
        {isFavoriteSignal.value ? '❤️' : '🤍'}
      </button>
    </section>
  );
});
```
</CodeSandbox>

### 5. Tasks and Invoking Server Code

In Qwik, a [task](/docs/core/tasks/#usetask) is work that needs to happen when a state changes. (This is similar to an "effect" in other frameworks.) In this example, we use the task to invoke code on the server.

1. Import `useTask$` from `qwik` and `server$` from `qwik-router`.
   ```tsx /useTask\$/ /server\$/
   import { component$, useSignal, useTask$ } from "@qwik.dev/core";
   import {
     routeLoader$,
     Form,
     routeAction$,
     server$,
   } from '@qwik.dev/router';
   ```

2. Create a new task that tracks the `isFavoriteSignal` state:
   ```tsx /useTask\$/
   useTask$(({ track }) => {});
   ```
3. Add a `track` call to re-execute the task on `isFavoriteSignal` state change:
   ```tsx /track/
   useTask$(({ track }) => {
     track(() => isFavoriteSignal.value);
   });
   ```
4. Add the work that you want to execute on state change:
   ```tsx {3}
   useTask$(({ track }) => {
     track(() => isFavoriteSignal.value);
     console.log('FAVORITE (isomorphic)', isFavoriteSignal.value);
   });
   ```
5. If you want to have work happen on the server only, wrap it in `server$()`
   ```tsx /server\$/ {4-6}
   useTask$(({ track }) => {
     track(() => isFavoriteSignal.value);
     console.log('FAVORITE (isomorphic)', isFavoriteSignal.value);
     server$(() => {
       console.log('FAVORITE (server)', isFavoriteSignal.value);
     })();
   });
   ```

NOTE:

- The body of `useTask$` is executed on both the server and the client (isomorphic).
- On SSR, the server prints `FAVORITE (isomorphic) false` and `FAVORITE (server) false`.
- When the user interacts with favorite, the client prints `FAVORITE (isomorphic) true` and the server prints `FAVORITE (server) true`.

For reference, the complete code snippet for this section is as follows:

<CodeSandbox src="/src/routes/demo/getting-started/05-tasks/index.tsx" style={{ height: '10em' }} maxHeight={500}>
```tsx {32-38} title="src/routes/joke/index.tsx"
import { component$, useSignal, useTask$ } from '@qwik.dev/core';
import {
  routeLoader$,
  Form,
  routeAction$,
  server$,
} from '@qwik.dev/router';

export const useDadJoke = routeLoader$(async () => {
  const response = await fetch('https://icanhazdadjoke.com/', {
    headers: { Accept: 'application/json' },
  });
  return (await response.json()) as {
    id: string;
    status: number;
    joke: string;
  };
});

export const useJokeVoteAction = routeAction$(
  (props) => {
    console.log('VOTE', props);
  },
  { invalidate: [useDadJoke] }
);

export default component$(() => {
  const isFavoriteSignal = useSignal(false);
  // Calling our `useDadJoke` hook, will return a reactive signal to the loaded data.
  const dadJokeSignal = useDadJoke();
  const favoriteJokeAction = useJokeVoteAction();
  useTask$(({ track }) => {
    track(() => isFavoriteSignal.value);
    console.log('FAVORITE (isomorphic)', isFavoriteSignal.value);
    server$(() => {
      console.log('FAVORITE (server)', isFavoriteSignal.value);
    })();
  });
  return (
    <section class="section bright">
      <p>{dadJokeSignal.value.joke}</p>
      <Form action={favoriteJokeAction}>
        <input type="hidden" name="jokeID" value={dadJokeSignal.value.id} />
        <button name="vote" value="up">
          👍
        </button>
        <button name="vote" value="down">
          👎
        </button>
      </Form>
      <button
        onClick$={() => (isFavoriteSignal.value = !isFavoriteSignal.value)}
      >
        {isFavoriteSignal.value ? '❤️' : '🤍'}
      </button>
    </section>
  );
});
```
</CodeSandbox>

### 6. Styling

Styling is an important part of any application. Qwik provides a way to associate and scope styles with your component.

To add styles:

1. Create a new file `src/routes/joke/index.css`:

   ```css
   p {
     font-weight: bold;
   }

   form {
     float: right;
   }
   ```

2. import the styles in `src/routes/joke/index.tsx`:
    ```tsx
    import styles from "./index.css?inline";
    ```
3. Import `useStylesScoped$` from `qwik`.
   ```tsx /useStylesScoped$/
   import { component$, useSignal, useStylesScoped$, useTask$ } from "@qwik.dev/core";
4. Tell the component to load the styles:
   ```tsx
   useStylesScoped$(styles);
   ```

Code explanation:

- The `?inline` query parameter tells Vite to inline the styles into the component.
- The `useStylesScoped$` call tells Qwik to associate the styles with the component only (scoping).
- Styles are only loaded if they are not already inlined as part of SSR and only for the first component.

For reference, the complete code snippet for this section is as follows:

<CodeSandbox maxHeight={500} src="/src/routes/demo/getting-started/06-styling/index.tsx" style={{ height: '10em' }}>
```tsx /useStylesScoped\$/#a /styles/#b title="src/routes/joke/index.tsx"
import {
  component$,
  useSignal,
  useStylesScoped$,
  useTask$,
} from '@qwik.dev/core';
import {
  routeLoader$,
  Form,
  routeAction$,
  server$,
} from '@qwik.dev/router';
import styles from './index.css?inline';

export const useDadJoke = routeLoader$(async () => {
  const response = await fetch('https://icanhazdadjoke.com/', {
    headers: { Accept: 'application/json' },
  });
  return (await response.json()) as {
    id: string;
    status: number;
    joke: string;
  };
});

export const useJokeVoteAction = routeAction$(
  (props) => {
    console.log('VOTE', props);
  },
  { invalidate: [useDadJoke] }
);

export default component$(() => {
  useStylesScoped$(styles);
  const isFavoriteSignal = useSignal(false);
  // Calling our `useDadJoke` hook, will return a reactive signal to the loaded data.
  const dadJokeSignal = useDadJoke();
  const favoriteJokeAction = useJokeVoteAction();
  useTask$(({ track }) => {
    track(() => isFavoriteSignal.value);
    console.log('FAVORITE (isomorphic)', isFavoriteSignal.value);
    server$(() => {
      console.log('FAVORITE (server)', isFavoriteSignal.value);
    })();
  });
  return (
    <section class="section bright">
      <p>{dadJokeSignal.value.joke}</p>
      <Form action={favoriteJokeAction}>
        <input type="hidden" name="jokeID" value={dadJokeSignal.value.id} />
        <button name="vote" value="up">👍</button>
        <button name="vote" value="down">👎</button>
      </Form>
      <button
        onClick$={() => (isFavoriteSignal.value = !isFavoriteSignal.value)}
      >
        {isFavoriteSignal.value ? '❤️' : '🤍'}
      </button>
    </section>
  );
});
```
</CodeSandbox>

### 7. Preview

Up until now, you've been using the dev server to make your application.

This is great to see your changes in real time, but it doesn't work the same way as in production:

- Each file is loaded individually, which may cause waterfalls in the network tab.
- There is no speculative loading of bundles, so there may be a delay on the first interaction.

To make sure everything is ready for production and eliminate these issues, you can run your app in preview:

1. Run `preview` command to create a production build and run it.

<PackageManagerTabs>
<span q:slot="pnpm">
```shell
pnpm run preview
```
</span>
<span q:slot="npm">
```shell
npm run preview
```
</span>
<span q:slot="yarn">
```shell
yarn run preview
```
</span>
<span q:slot="bun">
```shell
bun run preview
```
</span>
</PackageManagerTabs>

NOTE:

- Your application should now have a production build running on localhost:4173.
- If you interact with the application now, the network tab of the dev tools should show that the bundles have been [preloaded](/docs/advanced/speculative-module-fetching/).

## Review

Congratulations, you've learned a lot about Qwik!
For more on just how much you can achieve with Qwik, check out the dedicated docs on each of the topics touched on in this tutorial:

- [Project structure](/docs/(qwikrouter)/project-structure/index.mdx)
- [Directory-based routing](/docs/(qwikrouter)/routing/index.mdx#directory-based-routing)
- [Component](/docs/(qwik)/core/overview/index.mdx)
- [Route loaders](/docs/(qwikrouter)/route-loader/index.mdx)
- [Form actions](/docs/(qwikrouter)/action/index.mdx) && [zod validation](/docs/(qwikrouter)/action/index.mdx#validation-and-type-safety)
- [State management](/docs/(qwik)/core/state/index.mdx)
- [Tasks](/docs/(qwik)/core/tasks/index.mdx#use-usetask-when-you-need-to)
- [Preloading](/docs/(qwikrouter)/advanced/speculative-module-fetching/index.mdx)
