Add a New Qwik Project

The code for this example is available on GitHub:

Supported Features

We'll be using an Nx Plugin for Qwik called qwik-nx.

✅ Run Tasks ✅ Cache Task Results ✅ Share Your Cache ✅ Explore the Graph ✅ Distribute Task Execution ✅ Integrate with Editors ✅ Automate Updating Nx ✅ Enforce Module Boundaries ✅ Use Task Executors ✅ Use Code Generators ✅ Automate Updating Framework Dependencies

Install the qwik-nx Plugin

Install the qwik-nx plugin:

npm i --save-dev qwik-nx

Nx Version Compatibility

You can find a compatibility matrix for qwik-nx here: https://github.com/qwikifiers/qwik-nx#qwik-nx--nx-compatibility-chart.
You can use this to help you understand which version of qwik-nx you should install based on the version of nx you are currently using.
If you need help finding the version of nx you are currently using, run nx report.

Create the application

Let's generate a new application using qwik-nx.

nx g qwik-nx:app todo

Create a library

Let's generate a new library using qwik-nx.

nx g qwik-nx:lib data-access

Create a Context in the library

We'll add a Context to the library to store some state.

Create a new file libs/data-access/src/lib/todo.context.tsx with the following content:

import { component$, createContextId, Slot, useContextProvider, useStore, } from '@builder.io/qwik'; export interface Todo { id: number; message: string; } interface TodoStore { todos: Todo[]; lastId: number; } export const TodoContext = createContextId<TodoStore>('todo.context'); export const TodoContextProvider = component$(() => { const todoStore = useStore<TodoStore>({ todos: [], lastId: 0, }); useContextProvider(TodoContext, todoStore); return <Slot />; });

We'll use this context to store the state for our application.

Let's create a new file to handle some of the logic for our application.

Create libs/data-access/src/lib/todo.ts and add the following:

import { Todo } from './todo.context'; // A rudimentary in-mem DB that will run on the server interface DB { store: Record<string, any[]>; get: (storeName: string) => any[]; set: (storeName: string, value: any[]) => boolean; add: (storeName: string, value: any) => boolean; } export const db: DB = { store: { todos: [] }, get(storeName) { return db.store[storeName]; }, set(storeName, value) { try { db.store[storeName] = value; return true; } catch (e) { return false; } }, add(storeName, value) { try { db.store[storeName].push(value); return true; } catch (e) { return false; } }, }; export function getTodos() { // A network request or db connection could be made here to fetch persisted todos // For illustrative purposes, we're going to seed a rudimentary in-memory DB if it hasn't been already // Then return the value from it if (db.get('todos')?.length === 0) { db.set('todos', [ { id: 1, message: 'First todo', }, ]); } const todos: Todo[] = db.get('todos'); const lastId = [...todos].sort((a, b) => b.id - a.id)[0].id; return { todos, lastId }; } export function addTodo(todo: { id: string; message: string }) { const success = db.add('todos', { id: parseInt(todo.id), message: todo.message, }); return { success }; }

Update libs/data-access/src/index.ts to export our new context and methods:

export * from './lib/todo.context'; export * from './lib/todo';

Generate a Route

Next, let's generate a route to store the logic for the application.

nx g qwik-nx:route --name=todo --project=todo

We will use our new context Update the new route file (apps/todo/src/routes/todo/index.tsx) to the following:

import { component$, useContext, useTask$ } from '@builder.io/qwik'; import { Form, routeAction$, routeLoader$, z, zod$, } from '@builder.io/qwik-city'; import { addTodo, getTodos, TodoContext } from '@acme/data-access'; export const useGetTodos = routeLoader$(() => getTodos()); export const useAddTodo = routeAction$( (todo) => addTodo(todo), zod$({ id: z.string(), message: z.string() }) ); export default component$(() => { const todoStore = useContext(TodoContext); const persistedTodos = useGetTodos(); const addTodoAction = useAddTodo(); useTask$(({ track }) => { track(() => persistedTodos.value); if (persistedTodos.value) { todoStore.todos = persistedTodos.value.todos; todoStore.lastId = todoStore.lastId > persistedTodos.value.lastId ? todoStore.lastId : persistedTodos.value.lastId; } }); return ( <div> <h1>Todos</h1> {todoStore.todos.map((t) => ( <div key={`todo-${t.id}`}> <label> <input type="checkbox" /> {t.message} </label> </div> ))} <Form action={addTodoAction}> <input type="hidden" name="id" value={todoStore.lastId + 1} /> <input type="text" name="message" /> <button type="submit">Add</button> </Form> {addTodoAction.value?.success && <p>Todo added!</p>} </div> ); });

Build and Serve the Application

To serve the application, run the following command and then navigate your browser to http://localhost:4200/todo

nx serve todo

To build the application, run the following command:

nx build todo

More Documentation