Free Nuxt.js Tutorial — Vue Mastery Course
Free Nuxt.js Tutorial — Vue Mastery Course
731 4
In this Nuxt tutorial, a free lesson from the Vue Mastery course, we’ll build
an application together and learn about the folder structure that Nuxt.js
gives us out of the box. If you’re not sure why you might use Nuxt.js, you
might check out my previous article on 7 problems you can avoid by using
Nuxt.js.
HOW TO: Create a Nuxt.js app
Search Write
Prerequisite Knowledge
From this point forward I’m going to assume that you’re comfortable with
HTML, CSS, JavaScript basics, Vue.js, Vue Router, and Vuex. If you’re not
familiar with these Vue topics, I recommend you stop here and take our Real
World Vue.js course and our Mastering Vuex course.
npx is a tool intended to help round out the experience of using packages
from the npm registry — the same way npm makes it easy to install and
manage dependencies hosted on the registry, npx makes it easy to use CLI
tools and other executables hosted on the registry. In our case, that’s create-
nuxt-app .
If Yarn is your package manager of choice you can run yarn create nuxt-app
real-world-nuxt . It’ll set you up in the same way.
After running either the npx or yarn command, Create Nuxt App will
prompt you with a few questions to get you setup with your app’s default
configuration.
Here is how we’re going to answer for our project, and I really encourage you
to code with me.
731 4
It will also create a git repository for you, and run npm install to fetch all
dependencies.
To run the app that was just created in development mode, we’ll need to cd
into the directory it created for us run the following command:
This will startup our Nuxt.js server in development mode. By default, the
project will serve from http://localhost:3000/ . Visiting that URL will show
you the scaffolded project with a link to Nuxt’s documentation.
This would be a good point to create our first git commit, and add a repo to
github (or wherever you like to store your source code).
Now we’re in a good position to start building the events app. Before we get
started, let’s go over the folder structure that was created for us by create-
nuxt-app .
If we had created just a Vue application using the Vue CLI, it would given us
just a /src directory and inside that a components directory. This leaves us
with a few questions:
Each of these folders will contain component .vue files, and they each start
out with a single default generated file, which together showed us the page
we saw when we launched the development server.
Here are the remaining five folders Nuxt generated for us.
/store
A folder to contain all of your app’s Vuex Store Files. Nuxt gives you two
different ways to create your store and we’ll dive deeper into how as the
course progresses.
/static
Use this directory for storing static assets. For example, robots.txt or your
favicon. Every single file in this directory is mapped to the server root,
usually at / .
/assets
This directory contains un-compiled assets such as Stylus or Sass files,
images, or fonts. By default, Nuxt uses vue-loader, file-loader and url-loader
webpack loaders for strong assets serving. If you don’t want assets to be
affected by webpack, use the static directory for storing those assets.
/plugins
This folder contains your JavaScript plugins that you want to run before
instantiating the root Vue.js Application. This directory is helpful when using
your own libraries or Vue plugins.
/middleware
Middleware lets you define custom functions that can be run before
rendering either a page or a group of pages (layouts). This folder contains
your application middleware for that purpose.
📜 /pages/index.vue
<img src="~/assets/logo.png">
When we build our project, if our image is >= 1 kb, it will use version hashes
for caching and render out:
<img src="/_nuxt/img/82f7965.png">
The 82f7965 is the hash we’re talking about. This is beneficial because if our
logo.png file changes in the future, but the name itself remains the same,
the hash will change, and thus the new logo will be loaded in our customer’s
browsers. Without the change of hash our customer’s browser may continue
to load the old image.
When we build our project, if our image is < 1 kb it will inline the image to
reduce http requests, looking something like this:
<img src="data:image/png;base64,R0l...">
Obviously where I put … it would be a much longer string, which contains all
the data for the image. By placing the image code right there on the screen,
we avoid an extra network request to fetch the file.
📜 /layout/default.vue
<template>
<div id="app">
<nuxt />
</div>
</template>
<style>
html {
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
font-family: 'Open Sans', sans-serif;
font-size: 16px;
line-height: 1.5;
}
#app {
box-sizing: border-box;
width: 500px;
padding: 0 20px 20px;
margin: 0 auto;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
a {
color: #39b982;
font-weight: 600;
background-color: transparent;
}
img {
border-style: none;
width: 100%;
}
h1,
h2,
h3,
h4,
h5,
h6 {
display: flex;
align-items: center;
font-family: 'Montserrat', sans-serif;
}
h1 {
font-size: 50px;
font-weight: 700;
}
h2 {
font-size: 38px;
font-weight: 700;
}
h3 {
font-size: 28px;
font-weight: 700;
}
h4 {
font-size: 21px;
font-weight: 700;
}
h5 {
font-size: 16px;
font-weight: 700;
}
h6 {
font-size: 15px;
font-weight: 700;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
.eyebrow {
font-size: 20px;
}
.-text-primary {
color: #39b982;
}
.-text-base {
color: #000;
}
.-text-error {
color: tomato;
}
.-text-gray {
color: rgba(0, 0, 0, 0.5);
}
.-shadow {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0,
0, 0, 0.13);
}
.badge {
display: inline-flex;
height: 26px;
width: auto;
padding: 0 7px;
margin: 0 5px;
background: transparent;
border-radius: 13px;
font-size: 13px;
font-weight: 400;
line-height: 26px;
}
.badge.-fill-gradient {
background: linear-gradient(to right, #16c0b0, #84cf6a);
color: #fff;
}
button,
label,
input,
optgroup,
select,
textarea {
display: inline-flex;
font-family: 'Open sans', sans-serif;
font-size: 100%;
line-height: 1.15;
margin: 0;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: none;
}
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
border-style: none;
padding: 0;
}
button:-moz-focusring,
[type='button']:-moz-focusring,
[type='reset']:-moz-focusring,
[type='submit']:-moz-focusring {
outline: 2px solid #39b982;
}
label {
color: rgba(0, 0, 0, 0.5);
font-weight: 700;
}
input,
textarea {
box-sizing: border-box;
border: solid 1px rgba(0, 0, 0, 0.4);
}
textarea {
width: 100%;
overflow: auto;
font-size: 20px;
}
[type='checkbox'],
[type='radio'] {
box-sizing: border-box;
padding: 0;
}
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
[type='search'] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}
[type='text'],
[type='number'],
[type='search'],
[type='password'] {
height: 52px;
width: 100%;
padding: 0 10px;
font-size: 20px;
}
[type='text']:focus,
[type='number']:focus,
[type='search']:focus,
[type='password']:focus {
border-color: #39b982;
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
[hidden] {
display: none;
}
.error {
border: 1px solid red;
}
select {
width: 100%;
height: 52px;
padding: 0 24px 0 10px;
vertical-align: middle;
background: #fff
url("data:image/svg+xml;charset=utf8,%3Csvg
xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath
fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E")
no-repeat right 12px center;
background-size: 8px 10px;
border: solid 1px rgba(0, 0, 0, 0.4);
border-radius: 0;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
select:focus {
border-color: #39b982;
outline: 0;
}
select:focus::ms-value {
color: #000;
background: #fff;
}
select::ms-expand {
opacity: 0;
}
.field {
margin-bottom: 24px;
}
.error {
border: 1px solid red;
}
.errorMessage {
color: red;
}
</style>
📜 /pages/index.vue
<template>
<div>
<h1>Events</h1>
</div>
</template>
Wonderful!
📜 /pages/create.vue
<template>
<div>
<h1>Create An Event</h1>
</div>
</template>
If we dive into our browser, we can indeed navigate to both pages even
though we haven’t created or modified a router.js file. Nuxt.js is doing the
routing for us.
Step 6 — Create a NavBar Component for Navigation
Next, we’ll delete the /component/Logo.vue file that the Nuxt generator
created for us, and create our own component. Notice that we’re using the
nuxt-link to generate our links instead of router-link .
📜 /component/NavBar.vue
<template>
<div class="nav">
<nuxt-link to="/" class="brand">Real World Events</nuxt-link>
<nav>
<nuxt-link to="/">List</nuxt-link> |
<nuxt-link to="/create">Create</nuxt-link>
</nav>
</div>
</template>
<style scoped>
.brand {
font-family: 'Montserrat', sans-serif;
font-weight: 700;
font-size: 1.5em;
color: #39b982;
text-decoration: none;
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
}
.nav .nav-item {
box-sizing: border-box;
margin: 0 5px;
color: rgba(0, 0, 0, 0.5);
text-decoration: none;
}
.nav .nav-item.router-link-exact-active {
color: #39b982;
border-bottom: solid 2px #39b982;
}
.nav a {
display: inline-block;
}
</style>
Now we need to use this component inside our layout, just like we would
include any other component.
📜 /layouts/default.vue
<template>
<div id="app">
<nav-bar/>
<nuxt/>
</div>
</template>
<script>
import NavBar from "~/components/NavBar.vue";
export default {
components: {
NavBar
}
};
</script>
<style>
...
</style>
Now that we’ve made these changes we can jump into the browser and see
our Nuxt application in action.
⏪ To Re-Vue
In this tutorial we learned how to create a Nuxt.js project, about the different
directories Nuxt.js creates for you, and how to start building page
components and link them together. If you’d like to continue learning Nuxt
with us, you might consider subscribing to Vue Mastery’s Scaling Vue with
Nuxt course.
Web Development Vuejs Nuxtjs JavaScript
Responses (4)
To respond to this story,
get the free Medium app.
Ayodotun Ajala
Sep 6, 2019
First time I am learning nuxt.js and this article just demystified it for me. Wish I could give more than 50claps.
Thank you so much.
Anthony Gore
Mar 29, 2019
Soleymanian Usc
Jun 29, 2022
Reactivity 🎆
The Best Explanation of JavaScript What’s next for Vue in 2025?
Discover the latest Vue tools and updates for
2025 to enhance your development workflo…
By understanding what reactivity is and how it
works, you can improve your development…
10 reasons to use Nuxt.js for your 7 Problems you can avoid by using
next web application Nuxt.js for your next Vue app
If you’re a Vue.js developer, by now you’ve Vue.js is a great choice as a framework for
probably heard of Nuxt.js. But you might not… your application. But, there are a few…
Mar 20, 2018 6K 17 Jan 29, 2019 684 3
See all from Gregg Pollack See all from Vue Mastery
Productivity 🔥
8 Modern Dev Tools to 100X Your State Management in Vue 3: Pinia
vs Vuex
In today’s fast-paced tech world, developers When developing complex applications with
constantly seek tools to streamline their… Vue.js, managing the state of your applicatio…
Lists
Optimizing Pinia: Best Practices for Jeff Bezos Says the 1-Hour Rule
Faster Vue.js State Management Makes Him Smarter. New…
Pinia is the official state management library Jeff Bezos’s morning routine has long
for Vue 3, designed as a lighter, more moder… included the one-hour rule. New…