Skip to content

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

DoDon'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 dataexport default { myData: ... }
Share data app-wideLego.globals.myData = ...
Read global in template[[ global.myData ]]
Read global in JSLego.globals.myData
Get route params in template[[ $route.params.id ]]
Get route params in JSthis.$route.params.id
Get query string in template[[ $route.query.tab ]]
Get query string in JSthis.$route.query.tab
Notify parent block in template@click="() => $emit('event-name', data)"
Notify parent block in JSthis.$emit('event-name', data)
Navigate with stateSet 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

Get Help


You Did It! 🚀

You've built a complete multi-page app with LegoDOM.

Explore More Guides →

Released under the MIT License.