Step 5: State & Globals
You've built a complete app! Now let's understand how state management works in LegoDOM and explore advanced patterns.
Two Levels of State
1. Block State (Local)
Each block has its own reactive state defined in the <script> section:
javascript
export default {
// This is local to THIS block instance
count: 0,
items: [],
increment() {
this.count++; // Triggers re-render of THIS block only
}
}2. Global State (Lego.globals)
Shared across ALL blocks. Perfect for user sessions, themes, shopping carts, etc.
javascript
// In app.js or any block
Lego.globals.user = { name: 'John' };
Lego.globals.theme = 'dark';
Lego.globals.cart = [];
// In any block
console.log(Lego.globals.user.name); // 'John'Accessing Globals in Blocks
In JavaScript (Methods)
javascript
export default {
mounted() {
// Direct access
console.log(Lego.globals.user);
},
toggleTheme() {
Lego.globals.theme = Lego.globals.theme === 'dark' ? 'light' : 'dark';
}
}In Templates
Use the global keyword:
html
<template>
<p>Hello, [[ global.user.name ]]!</p>
<div class="[[ global.theme ]]">
Content styled by theme
</div>
<button b-show="global.user">Logout</button>
</template>Route State ($route)
Access current route information:
javascript
// Available in any block
this.$route.url // '/users/42?tab=posts'
this.$route.route // '/users/:id'
this.$route.params // { id: '42' }
this.$route.query // { tab: 'posts' }html
<template>
<h1>User [[ $route.params.id ]]</h1>
<p>Current tab: [[ $route.query.tab ]]</p>
</template>State Patterns
Pattern 1: User Authentication
javascript
// app.js
Lego.globals.user = null; // Initialize
// login-page.lego
handleLogin() {
Lego.globals.user = { name: 'John', token: 'abc123' };
this.$go('/dashboard').get();
}
// Any protected block
mounted() {
if (!Lego.globals.user) {
this.$go('/login').get();
}
}Pattern 2: Theme Switching
javascript
// app.js
Lego.globals.theme = localStorage.getItem('theme') || 'light';
// theme-toggle.lego
<template>
<button @click="toggle()">
[[ global.theme === 'dark' ? '☀️' : '🌙' ]]
</button>
</template>
<script>
export default {
toggle() {
const newTheme = Lego.globals.theme === 'dark' ? 'light' : 'dark';
Lego.globals.theme = newTheme;
localStorage.setItem('theme', newTheme);
document.body.className = newTheme;
}
}
</script>Pattern 3: Shopping Cart
javascript
// app.js
Lego.globals.cart = [];
// product-card.lego
addToCart() {
Lego.globals.cart.push({
id: this.product.id,
name: this.product.name,
price: this.product.price
});
}
// cart-icon.lego
<template>
<span class="cart">
🛒 [[ global.cart.length ]]
</span>
</template>Pattern 4: Fetching Data
javascript
export default {
users: [],
loading: true,
error: null,
async mounted() {
try {
const response = await fetch('/api/users');
this.users = await response.json();
} catch (e) {
this.error = e.message;
} finally {
this.loading = false;
}
}
}html
<template>
<div b-show="loading">Loading...</div>
<div b-show="error" class="error">[[ error ]]</div>
<ul b-show="!loading && !error">
<li b-for="user in users">[[ user.name ]]</li>
</ul>
</template>Cross-Block Communication
Using $emit (Child to Parent)
html
<!-- child-block.lego -->
<template>
<button @click="notifyParent()">Click Me</button>
</template>
<script>
export default {
notifyParent() {
this.$emit('custom-event', { message: 'Hello from child!' });
}
}
</script>html
<!-- parent-block.lego -->
<template>
<child-block @custom-event="handleEvent(event)"></child-block>
</template>
<script>
export default {
handleEvent(event) {
console.log(event.detail.message); // 'Hello from child!'
}
}
</script>Using $ancestors (Access Parent Element + State)
javascript
// In a deeply nested block
mounted() {
// Get state from a specific ancestor by tag name
const parentState = this.$ancestors('user-profile');
console.log(parentState.state.userId);
}Best Practices
| Do | Don't |
|---|---|
✅ Use Lego.globals for truly global state (user, theme) | ❌ Put everything in globals |
| ✅ Keep block state local when possible | ❌ Over-engineer state management |
✅ Initialize globals in app.js | ❌ Initialize globals in random blocks |
✅ Use $emit for child→parent communication | ❌ Reach into child block internals |
Complete State Cheatsheet
| I want to... | Use this |
|---|---|
| Store block-local data | export default { myData: ... } |
| Share data app-wide | Lego.globals.myData = ... |
| Read global in template | [[ global.myData ]] |
| Read global in JS | Lego.globals.myData |
| Get route params in template | [[ $route.params.id ]] |
| Get route params in JS | this.$route.params.id |
| Get query string in template | [[ $route.query.tab ]] |
| Get query string in JS | this.$route.query.tab |
| Notify parent block in template | @click="() => $emit('event-name', data)" |
| Notify parent block in JS | this.$emit('event-name', data) |
| Navigate with state | Set globals before $go() |
What's Next?
🎉 Congratulations! You've completed the LegoDOM tutorial!
You now know how to:
- Set up a project from scratch
- Create beautiful blocks
- Navigate between pages
- Share state across your app
Continue Learning
- Blocks Deep Dive – Advanced block patterns
- Directives Reference – All
b-*directives explained - Routing Guide – Surgical swaps and advanced routing
- API Reference – Complete API documentation