Boise Code Camp 2026
Nerando Johnson · @Nerajno
@Nerajno
Opinions are mine. Examples simplified for clarity. Vue 3 Composition API throughout.
Welcoming space. Be kind, curious, constructive. Questions welcome at any time.
Sponsors & Networking — "Links run di world" 🇯🇲
Note to Self
"May your console errors be zero
and your reactivity be perfect."
Inspired by Shepard's Prayer · May 5, 1961
Source: State of JavaScript 2024 survey · State of Vue.js Report 2025
of all devs use JavaScript — #1 language 12+ years running (Stack Overflow 2024)
Vue.js at ~45% usage — firmly the #2 frontend framework globally behind React
live sites built with Vue — doubled in two years, still growing
Vite: 69% adoption · Most loved build tool. Nuxt: #2 meta-framework. Vue sentiment steady at 65–75% positive.
Devs jump into Vue before understanding the JavaScript powering it
Reactivity bugs feel like "Vue bugs" — they're JS bugs in a costume
npm create vue@latest is line 1 of too many tutorials
The issue isn't Vue — it's the missing JS foundation underneath it
The Real Problem
// What tutorials start with:
import { createApp } from 'vue'
// What you actually need first:
const foundation = {
variables: '✓ understand scope & types',
arrowFns: '✓ lexical this binding',
asyncAwait: '✓ promises under the hood',
destructuring: '✓ props & composable returns',
modules: '✓ every import you write',
}
// Vue = JavaScript + opinions ✨
Framework ≠ JavaScript. Framework = JavaScript + opinions.
JS Fundamentals — the 11 concepts you need
Learning Bottom Up — why order matters
Fundamental → Vue 3 — every concept applied
Examples — real code, real patterns
Recommendations — what to do after this talk
Non-negotiable before you go deep on Vue 3
JavaScript Fundamentals — scope, closures, async, modules
Vue 3 Primitives — ref, reactive, computed, watch
Vue 3 Patterns — composables, Pinia, Vue Router, lifecycle
Production Apps — Nuxt, SSR, performance, scale
Skip the base, shake the tower. Every Composition API pattern traces back to vanilla JS.
The atoms of every JS program — and every Vue component
let = reassignable · const = fixed reference · Know your primitives: string, number, boolean, null, undefined, symbol, bigint
const name = "Nerando"; // string
let score = 42; // number
let isActive = true; // boolean
const user = { id: 1 }; // object
const tags = ["vue"]; // array
import { ref, reactive } from 'vue'
// ref wraps primitives
const name = ref("Nerando")
const score = ref(42)
const isActive = ref(true)
// reactive wraps objects
const user = reactive({
id: 1,
role: "developer"
})
// .value for ref, direct for reactive
console.log(name.value) // "Nerando"
console.log(user.role) // "developer"
Backticks that power your bindings
Backticks (`) embed expressions: `Hello, ${name}!`. Multiline, expression-aware, composable.
const role = "developer"
const msg = `Hello, ${name}!
You are a ${role}.`
// Expression inside
const total =
`$${(qty * 9.99).toFixed(2)}`
// Computed string
const greeting = computed(() =>
`Welcome back, ${user.name}!`
)
// Dynamic :class binding
// in template
// :class="`btn ${isActive
// ? 'btn--active'
// : 'btn--idle'}`"
// Dynamic route path
const path = computed(() =>
`/users/${userId.value}/profile`
)
The backbone of the Composition API
this — critical for Composition API// Traditional
function add(a, b) { return a+b }
// Arrow — implicit return
const add = (a, b) => a + b
// Arrow with body
const greet = (name) => {
return `Hello, ${name}`
}
// In array methods
const evens =
[1,2,3,4].filter(n => n%2===0)
const count = ref(0)
// computed uses arrow callback
const doubled = computed(
() => count.value * 2
)
// watch uses arrow callback
watch(count, (newVal, oldVal) => {
console.log(`${oldVal} → ${newVal}`)
})
// event handler in <script setup>
const handleClick = () => {
count.value++
}
How composables return values to you
const user = {
name: "Nerando",
role: "dev",
city: "Atlanta"
}
// Destructure
const { name, role } = user
// Rename
const { name: devName } = user
// Default value
const { theme = "dark" } = user
// Array destructure
const [first,,third] = ["a","b","c"]
// defineProps destructuring
const { title, count } =
defineProps(['title', 'count'])
// Composable return destructure
const { data, error, pending } =
useFetch('/api/users')
// Route params
const { params, query } = useRoute()
// Pinia store destructure
// (use storeToRefs to keep reactivity!)
const { user, isAuth } =
storeToRefs(useAuthStore())
Vue 3 gotcha: destructuring a reactive() object breaks reactivity — use toRefs() or storeToRefs()
4 of 11 done. Questions before we continue?
@Nerajno.map() .filter() .reduce() — your v-for fuel
const nums = [1, 2, 3, 4, 5]
// transform each item
const doubled =
nums.map(n => n * 2)
// [2, 4, 6, 8, 10]
// keep matching items
const evens =
nums.filter(n => n % 2 === 0)
// [2, 4]
// accumulate
const sum =
nums.reduce((acc,n) => acc+n, 0)
// 15
<!-- template -->
<li
v-for="item in activeItems"
:key="item.id"
>
{{ item.name }}
</li>
<!-- script setup -->
<script setup>
const items = ref([...])
const activeItems = computed(() =>
items.value
.filter(i => i.active)
.sort((a, b) =>
a.name.localeCompare(b.name))
)
</script>
onMounted, useAsyncData, and every API call you'll ever write
// Promise chain
fetch('/api/user')
.then(r => r.json())
.then(d => console.log(d))
.catch(e => console.error(e))
// async/await — cleaner
async function getUser() {
try {
const res = await fetch('/api/user')
return await res.json()
} catch (err) {
console.error('Failed:', err)
}
}
const data = ref(null)
const error = ref(null)
const loading = ref(false)
onMounted(async () => {
loading.value = true
try {
const res = await fetch('/api/data')
data.value = await res.json()
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
})
Nuxt users: useAsyncData() and useFetch() are built on this exact pattern
Every composable is a module. Every import is this.
// math.js — named exports
export const add = (a, b) => a + b
export const PI = 3.14159
// user.js — default export
export default function createUser(n) {
return { name: n, active: true }
}
// app.js
import createUser from './user.js'
import { add } from './math.js'
// composables/useCounter.js
import { ref } from 'vue'
export function useCounter(initial = 0) {
const count = ref(initial)
const inc = () => count.value++
const dec = () => count.value--
const reset = () =>
count.value = initial
return { count, inc, dec, reset }
}
// MyComponent.vue <script setup>
import { useCounter }
from '@/composables/useCounter'
const { count, inc } = useCounter(10)
Blueprints for custom directives & plugins
class ApiClient {
constructor(baseURL) {
this.baseURL = baseURL
this.headers = {
'Content-Type': 'application/json'
}
}
async get(path) {
const res = await fetch(
`${this.baseURL}${path}`,
{ headers: this.headers }
)
return res.json()
}
}
// plugins/apiClient.js
class ApiClient { /* ... */ }
export default {
install(app, options) {
const client =
new ApiClient(options.baseURL)
// Available via inject()
app.provide('api', client)
// Or as global property
app.config.globalProperties.$api =
client
}
}
// main.js
app.use(ApiClientPlugin, {
baseURL: 'https://api.example.com'
})
Navigate API responses safely — every time
// Without optional chaining
const city =
user && user.address
? user.address.city
: undefined
// With optional chaining ✨
const city = user?.address?.city
// Call functions safely
const str = name?.toUpperCase()
// Nullish coalescing fallback
const display = city ?? "Atlanta"
<!-- Template — safe access -->
<p>{{ user?.profile?.bio }}</p>
<p>{{ post?.author?.name ?? 'Anonymous' }}</p>
<!-- Script setup -->
const city = computed(() =>
store.user?.address?.city ?? 'N/A'
)
// In watch — handle undefined initial
watch(user, (u) => {
if (u?.isAdmin) enableAdminMenu()
})
@click is just addEventListener with Vue superpowers
// Native JS
el.addEventListener('click', (e) => {
console.log(e.target)
e.preventDefault()
e.stopPropagation()
})
// Event object properties
// e.target — element clicked
// e.currentTarget — listener element
// e.key — for keyboard events
// e.detail — for custom events
<!-- Basic -->
<button @click="handleClick">Go</button>
<!-- .prevent modifier -->
<form @submit.prevent="onSubmit">
<!-- .stop modifier -->
<div @click.stop="doSomething">
<!-- Emit custom events -->
<!-- child component -->
const emit = defineEmits(['update'])
emit('update', { value: 42 })
<!-- parent -->
<Child @update="handleUpdate" />
Fail gracefully. Your users deserve it.
// Synchronous
try {
riskyOperation()
} catch (err) {
console.error(err.message)
} finally {
cleanup()
}
// Async
async function load() {
try {
const data = await fetchData()
return data
} catch (err) {
// Handle fetch errors
notifyUser(err.message)
}
}
// In a parent/layout component
onErrorCaptured((err, instance, info) => {
console.error('Vue caught:', err)
errorLog.value = {
message: err.message,
component: info
}
return false // stop propagation
})
// Global handler in main.js
app.config.errorHandler = (err, vm, info) => {
// Send to Sentry, Datadog, etc.
trackError(err, info)
}
Every one of those is JavaScript. Every one shows up in your Vue 3 code today.
@NerajnoBuild these in vanilla JS first — then translate to Vue 3. See the 1:1 map.
🌤️
async/await · optional chaining · template literals · ref()
🎨
event handling · template literals · reactive() · v-model
✅
.map() .filter() · modules · composables · Pinia
All three available on GitHub — links in the comments after the talk.
Leave with a plan, not just slides
javascript.info is the best free resource for fundamentals. Read the "JavaScript Fundamentals" and "Advanced" sections before your next Vue project.
Open node_modules/vue/dist/vue.esm-browser.js — find ref() and read 20 lines. You'll see a plain JS class. Demystify the magic.
Weather app → Color Picker → Todo. Vanilla JS first, Vue 3 second. Document every connection you find. Share it on dev.to or your blog.
Take one thing you've done in a component and extract it as a useX() composable. You'll immediately see which JS concepts power it.
Vue 3's Composition API is JavaScript. ref() is a class. computed() is a derived function. watch() is a callback on a closure. Composables are modules. Master the JS, and Vue becomes obvious.
| JavaScript Concept | Vue 3 Usage | Key API / Pattern |
|---|---|---|
| Variables & Data Types | Reactive state primitives | ref() / reactive() |
| Template Literals | Dynamic class & route binding | :class / computed() |
| Arrow Functions | Composition API callbacks | computed() / watch() |
| Destructuring | Composable & store returns | storeToRefs() / toRefs() |
| Array Methods | Filtered & sorted list rendering | v-for + computed() |
| Promises & Async/Await | Data fetching lifecycle | onMounted() / useFetch() |
| Modules & Exports | Composable architecture | useX() pattern |
| Classes & OOP | Plugins & provide/inject | app.use() / provide() |
| Optional Chaining | Safe template & store access | user?.profile?.bio |
| Event Handling | DOM & component events | @click / defineEmits() |
| try / catch / finally | Component error boundaries | onErrorCaptured() |
Questions / Fin
Nerando Johnson
@Nerajno · Boise Code Camp 2026
Bluesky
@nerajno.bsky.social
GitHub
Progress over Perfection. Let's go. 💚