# Welcome to the Qwik Rabbit Store! ## Introduction Welcome to the Qwik Rabbit Store tutorial! A full-featured E-Commerce application where the world can find the most adorable rabbits in the world powered by the speed of Qwik and Qwik City. We will build our website from the ground up and with each step we will learn how we can use Qwik and its primer meta-framework Qwik City to create a real-world application that you can show your mom. But enough yammering, let's begin! ![](https://i.imgur.com/l6KI7dr.png) ## Qwik First Steps Before Qwik, there was the emptiness. The emptiness of your favorite terminal, let’s go there and start by creating an empty project using one of the Qwik City presets. INSET: Install Node+Yarn. Qwik needs at least Node 16 ```bash npm create qwik@latest ``` > Note for contributors: Consider all images temporary, in the final version they will 100% change. ![Terminal with qwik creation prompt](https://i.imgur.com/82MF6hI.jpg) You will be prompted for the project’s name. Use whatever you want but this is the “Qwik Rabbit Store” after all. We recommend **“qwik-rabbit-store”**. ![Terminal showing the select starter prompt](https://i.imgur.com/vcHb5jN.jpg) Like with any good learning journey we will start with the basics. Press enter to select the default starter and then enter again to install the packages and create the project. ![Terminal showing Qwik app creation completion](https://i.imgur.com/F2tnYr9.jpg) We did it! But don’t stop now, follow the terminal instructions to start running your first (website|application). INSET: We can use any package manager to install the dependencies like npm, yarn, or pnpm. ![Qwik default root page](https://i.imgur.com/DUI8GhQ.jpg) That’s pretty, I love how that looks but that ain’t a rabbit store. We need to build ours yet, but before, let’s explore the files that we just created. ### Exploring a Qwik Project Fantastic! We are all set. Now open the newly created project in your favorite code editor. INSET: You can use VSCode, WebStorm, or whatever you like. Etc. { This section should describe in very broad terms how the project is organized Among other things: Behind the scenes, Qwik uses Vite Check the package.json No dependencies, just dev-dependencies Qwik City by default TypeScript and ESLint by default Scripts Public folder for assets Src Folder Components Routes Root files } ### Our first route {Not really new, here we will delete everything that the root route contains and explain the anatomy of a basic route. This is a good moment to introduce the component$ function in a summarized way and mention the $ just the necessary information to make the developer know that it is important. Finally, we will add an h1 and a title in the document head and show how the page changed in the browser. This is enough to make the developer comfortable that developing in Qwik is just as in any other framework} ## What will we build? { Here we can do a little tour to the final application hipping the user with what they will learn to build by completing the tutorial Because we need the full app this will be one of the last sections to write. } ## Routes ## Fetch and List ## Navigate to Details Qwik City scafold: ```md src/ |___components/ | |__counter.tsx | |___routes/ |__docs/ | |__overview/ | | |__index.mdx | | | |__index.mdx | |__index.tsx |__layouttsx ``` Now that we have x, y and z, let's show our customers some of the fine products they can buy in our store. Qwik City allows us to put data access endpoint handlers right inside our route files. To load our Rabbit Store products into the client code, we're going to add an onGet() handler function to the home page route. Add the following to your /src/routes/index.tsx file above the default export: ```ts interface Product { id: string; name: string; description: string; price: number; } interface Category { id: string; name: string; products: Array<Product>; } export const onGet: RequestHandler<Category[]> = async ({ params }) => { const categories: Category[] = [ { id: "1", name: "Food", products: [ { id: "1", name: "Baby carrots", description: "Great for baby rabbits.", price: 4.99 }, { id: "2", name: "Romaine Lettuce", description: "Fresh from the farm.", price: 3.29 } ] }, { id: "2", name: "Movies", products: [ { id: "1", name: "Rabbits are Forever", description: "James Bond, a rabbit, equipped with an armoury of hi-tech gadgets, infiltrates a Las Vegas carrot-smuggling ring in a bid to foil a plot to target Washington with a laser in space. However, as 007 prepares to tackle the evil Blofeld, the mastermind who threatens to destabilise the world, he is captivated by the delicious Tiffany Case - but is she really a double agent?", price: 14.99 }, { id: "2", name: "Who Framed Roger Rabbit?", description: "Down-on-his-luck private eye Eddie Valiant (Bob Hoskins) gets hired by cartoon producer R.K. Maroon (Alan Tilvern) to investigate an adultery scandal involving Jessica Rabbit (Kathleen Turner), the sultry wife of Maroon's biggest star, Roger Rabbit (Charles Fleischer). But when Marvin Acme (Stubby Kaye), Jessica's alleged paramour and the owner of Toontown, is found murdered, the villainous Judge Doom (Christopher Lloyd) vows to catch and destroy Roger.", price: 14.99 } ] }, { id: "2", name: "Clothing", products: [ { id: "1", name: "Track Suit", description: "It'll never slow you down.", price: 37.99 }, { id: "2", name: "AirSole Runners", description: "Put a spring in your hop.", price: 104.32 } ] } ]; return categories; }; ``` And, to access our data within our front end component, we'll add a useEndpoint() hook. Behind the scenes, this will make a fetch request to the back end onGet() function that we just added, and retrieve the store data. Add this to your code. export default component$(() => { const categoryData = useEndpoint<Category[]>(); return ( ... Finally, to display the data we just retrieved, we'll add a <Resource /> element to our JSX. This will handle the pending and error states for the data we got back from the server with useEndpoint(), and display the data when it arrives. Replace the return block in the default component with this: ```ts return ( <div class="prose"> <h1>The Qwik Rabbit Store</h1> <h2>Categories</h2> <Resource value={categoryData} onPending={() => <div>Loading...</div>} onRejected={() => <div>Error</div>} onResolved={(categories) => ( <> {categories.map(category => ( <div> <h2>{category.name}</h2> {category.products.map(product => ( <div> <h3>{product.name}</h3> <p> {product.description} </p> <div> ${product.price} </div> </div> ))} </div> ))} </> )} /></div> ); }); ``` The <Resource /> value prop takes the return value from our useEndpoint() call (categoryData). The onPending, onRejected and onResolved attributes of our <Resource /> element render different JSX based on the current status of categoryData. The contents of your /src/routes/index.tsx file should now look like this <Click to expand>. Now, let's head back over to the browser and see the result. Boom! The rabbits are going to love this. Just for fun, let’s take a look at the Network tab to see what Qwik is doing for us behind the scenes.. Now, let's make our products display a lot prettier by dressing it up with Tailwind CSS... --- --- ## Introduction Welcome to the Qwik Rabbit Store tutorial! A full-featured E-Commerce application where the world can find the most adorable rabbits in the world powered by the speed of Qwik and Qwik City. We will build our website from the ground up and with each step we will learn how we can use Qwik and its primer meta-framework Qwik City to create a real-world application that you can show your mom. But enough yammering, let's begin! ## Installation Open your terminal and move to the folder you want to create your project ``` npm create qwik@latest ``` It will prompt you for a project name. Call it `qwik-rabbit-store`. It will also ask you to select a starter. Select "Basic". Finally, it will ask whether you want to install dependencies. Hit enter to accept the default, "Yes". This creates a new directory called `qwik-rabbit-store`, and inside it, you'll find a brand new Qwik project. This project uses Qwik City, Qwik's lightning-fast file-based routing server. ## The generated project Now, open the project in your favorite IDE, and start the project in dev mode with: ```bash npm run dev ``` You'll see output in your terminal similar to: ```bash VITE v3.1.7 ready in 1167 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ``` Open the URL you see in your terminal (the port number might be different from the above) in your favorite web browser. You'll see the generated home page. ![](https://i.imgur.com/rJ6BofF.jpg) Back over in your IDE, let's take a look at the project structure. ``` public/ # Static files here. └── favicon.ico # http://example.com/favicon.ico src/ ├── components/ # Reusable components here. ├── routes/ # Route-specific components here. │ └── somePath/ │ └── index.tsx # https://example.com/somePath └── index.tsx # https://example.com ``` Open the `src/routes/index.tsx` file in your IDE. This is the code for the home page that's open in your web browser. Let's change it and see what happens. Replace the contents of this file with: ```jsx import { component$ } from "@builder.io/qwik"; import type { DocumentHead } from "@builder.io/qwik-city"; export default component$(() => { return ( <div> <h1> Welcome to the Qwik Rabbit Store! </h1> </div> ); }); export const head: DocumentHead = { title: "Welcome to the Qwik Rabbit Store!" }; ``` Back over in your browser, you'll see: ![](https://i.imgur.com/mTdqdBV.jpg) Qwik City gives us file-based routing. To see how this works create a new directory called `products` under the `src/routes` directory. ```bash mkdir src/routes ``` Inside this directory, create a new file called `index.tsx`, and add the following code: ```jsx import { component$ } from "@builder.io/qwik"; import type { DocumentHead } from "@builder.io/qwik-city"; export default component$(() => { return ( <div> <h1> Products will go here remarkably soon! </h1> </div> ); }); export const head: DocumentHead = { title: "Qwik Rabbit Store :: Products" }; ``` Now point your browser at `http://localhost:5173/products`. You'll see: ![](https://i.imgur.com/Co0OulD.jpg) Also, notice that we set the page title using our `head` export. This can be used to set metadata in `<head/>`. With Qwik City, to create a route, you first create a directory under `src/routes` named with the route name (like, `src/routes/products`), then put a Qwik JSX file in there called `index.tsx`. >Qwik City happily serves .ts, .tsx, .js, .jsx, .md, and .mdx; and supports a ton of other features (like route parameters, nested routes and much, much more). This tutorial covers everything. Keep reading! The performance-critical parts of Qwik City are written in Rust, and Qwik City support HMR in dev mode. It's ridiculously fast! **Note** All of our routes should has an index file, this is very usefull for handling a data loader architecture. Let's visit our first component ```js // routes/index.tsx import { component$ } from '@builder.io/qwik' export default component$(() => { return <>Hello World!</> }) ``` ## Our First Layout At any level in the routes folder we can define a special file called `layout`. It serves as a layout for one or more pages with a `layout` file adyacent to a common ancestor. Let's create a generic layout with a header and a footer: ``` ┌─────────────────────────────────────────────────┐ │ Header │ ├─────────────────────────────────────────────────┤ │ <ROUTE_SPECIFIC_CONTENT> │ │ │ │ │ │ │ ├─────────────────────────────────────────────────┤ │ Footer │ └─────────────────────────────────────────────────┘ ``` ## Tailwind (qwik add) ## on* endpoints ## Prisma ## Sign up/in/out ``` src/ ├── components/ │ ├── header.tsx # Header component implementation │ └── footer.tsx # Footer component implementation └── routes/ ├── layout.tsx # Layout using: <Header> and <Footer> └── index.tsx # https://example.com (home page) ``` ### Home Page Create: ```js // src/components/header.tsx import { component$ } from "@builder.io/qwik" export default component$(() => { return <>Header</> }) ``` ```js // src/components/footer.tsx import { component$ } from "@builder.io/qwik" export default component$(() => { return <>Footer</> }) ``` ```js // src/routes/layout.tsx import { component$, Slot } from "@builder.io/qwik" import Footer from "~/components/footer" import Header from "~/components/header" export default component$(() => { return ( <> <Header /> <Slot/> {/* <== This is where the route will be inserted */} <Footer /> </> ) }) ``` Update: ```js // src/routes/index.tsx import { component$ } from '@builder.io/qwik' export default component$(() => { return <>Home</> }) ``` Run: ```sh npm start ``` Go to: ```sh http://127.0.0.1:5173/ ``` See: ![[first-layout-home.png]] ### About Page Now create the about page: ``` src/ └── routes/ ├── layout.tsx ├── about/ │ └── index.tsx # https://example.com/about └── index.tsx # https://example.com ``` ```js // src/routes/about/index.tsx import { component$ } from "@builder.io/qwik" export default component$(() => { return <>About</> }) ``` Run: ```sh npm start ``` Go to: ```sh http://127.0.0.1:5173/about ``` See: ![[first-layout-about.png]] Here we are using the same layout for both pages (home and about). 🤯 #### Git Point ```sh git add . git commit -m "feat(routes/layout): add home & about routes" ``` ## Login Page Login pages have a simpler design: ``` ┌─────────────────────────────────────────────────┐ │ │ │ <LOGIN_SPECIFIC_CONTENT> │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────┘ ``` They don't need a footer or a header, for that reason we have to skip the layout we just created for this route. To do that we could: - skip the routes/layout inside the login folder creating a new layout and using it as a top layout [see](https://qwik.builder.io/qwikcity/layout/top/) - or grouping all the not login pages into a grouped layout [see](https://qwik.builder.io/qwikcity/layout/grouped/) - or use a specific named layout [see](https://qwik.builder.io/qwikcity/layout/named/) We choose the second option but feel free to choose your solution 😄 ``` src/ └── routes/ ├── (main)/ # the (name) folder will be ignored │ ├── layout.tsx # in the path │ ├── about/ │ │ └── index.tsx │ └── index.tsx └── login/ └── index.tsx ``` Let's create the (main) folder and move all our routes inside. Now create the login page: ```js // src/routes/login/index.tsx import { component$ } from "@builder.io/qwik" export default component$(() => { return <>Login</> }) ``` Run: ```sh npm start ``` Go to: ```sh http://127.0.0.1:5173 ``` Go to: ```sh http://127.0.0.1:5173/about ``` And verify that everything is working as before. Go to: ```sh http://127.0.0.1:5173/login ``` See: ![[first-login-page.png]] #### Git Point ```sh git add . git commit -m "feat(routes/login): add login page" ``` ## Our First Component Before we start please copy/paste some general styles to global.css: ```CSS /** src/global.css **/ /* == DEFAULTS == */ /* % scale 1em = 10px (optional) */ /* set width = content + padding + border */ html { font-size: 62.5%; box-sizing: border-box; } *, *::before, *::after { box-sizing: inherit; } /* remove default browser margin */ body { margin: 0; } /* avoid merge magins */ p, h1, h2, h3, h4, h5, h6 { margin-top: 0; } /* avoid unwanted spaces */ /* set max-width = real image width */ img { display: block; max-width: 100%; } /* remove default browser styling for ul */ ul { list-style: none; padding: 0; } /* == GENERAL == */ ``` ### Qwik Introduction Qwik uses JSX as template syntax, for that reason can be very React like but under the hood it takes a total different approach. Qwik delays downloading JS as much as possible and only retrieve the needed amount when a user event occurs. Besides it's not limited to functions and event handlers, even components can be lazy loaded! To do that, Qwik breaks the application into small chunks. We use the reserved `$` symbol to tell Qwik what those chunks are.