vwf .

Learn - Section: 5

Progressive tutorial. Start with Getting Started and work your way through each section

Building Real Applications

1. File-Based Routing (5 min)

VWF uses file structure to define routes automatically:

// File structure = URL structure
src/app/
├── page.js                 → /
├── about/page.js           → /about
├── dashboard/
│   ├── page.js            → /dashboard
│   └── settings/page.js   → /dashboard/settings

// Every page.js exports 'component'
export const component = () => {
  return div(
    h1('Dashboard'),
    // Page content
  );
};

// Navigation with router
import { router } from '../utils/fileBasedRouterV2.js';

Button({
  onclick: () => router.navigate('/about')
}, 'Go to About')

// Or use regular links (router handles them)
a({ href: '/dashboard' }, 'Dashboard')
💡Important: File structure IS your routing configuration. No manual route setup needed!
✅Best Practice: Use layout.js files to wrap child pages with shared UI (headers, sidebars, etc.)

2. Services & API Calls (5 min)

Keep API logic in service files, not components:

// File: services/userService.js
export const userService = {
  async getUser(id) {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
  },

  async updateUser(id, data) {
    const response = await fetch(`/api/users/${id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
    return response.json();
  }
};

// In your component
import { userService } from '../services/userService.js';

export const component = () => {
  const user = van.state(null);
  const loading = van.state(true);
  const error = van.state(null);

  // Load data
  userService.getUser(123)
    .then(data => {
      user.val = data;
      loading.val = false;
    })
    .catch(err => {
      error.val = err;
      loading.val = false;
    });

  return div(
    () => loading.val ? Spinner() : '',
    () => error.val ? ErrorMessage(error.val) : '',
    () => user.val ? UserProfile(user.val) : ''
  );
};
✅Best Practice: Separate concerns: Components = UI, Services = Data. Makes testing and maintenance easier!
⚠️Warning: Don't use fetch() directly in components. Always use service layer.

3. State Management Patterns (5 min)

For simple apps, van.state() is enough. For complex state, consider patterns:

// Pattern 1: State in parent component
export const component = () => {
  // Parent manages state
  const activeTab = van.state('home');
  const userData = van.state(null);

  return div(
    Header({ activeTab }),  // Pass state as prop
    Content({ activeTab, userData }),
    Footer()
  );
};

// Pattern 2: Shared state module
// File: state/appState.js
import van from 'vanjs-core';

export const appState = {
  user: van.state(null),
  theme: van.state('light'),
  notifications: van.state([])
};

// Import anywhere
import { appState } from '../state/appState.js';
div(() => appState.user.val?.name)

// Pattern 3: VanX for complex state (optional)
// Only use if you need bulk updates or vanX.list
import { stateFields } from 'vanjs-ext';

const state = stateFields({
  user: null,
  settings: {},
  data: []
});

// Update multiple at once
state.set({
  user: userData,
  settings: userSettings
});
💡Important: For 90% of apps, simple van.state() is perfect. Only use VanX if you need bulk state updates.
✅Best Practice: Keep state as local as possible. Only lift to parent when needed by multiple children.

4. Performance Best Practices (5 min)

VanJS is fast by default, but follow these patterns for optimal performance:

// ✅ Good: Simple reactive functions
const count = van.state(0);
div(() => `Count: ${count.val}`)

// ❌ Bad: Complex logic in reactive functions
div(() => {
  const result = items.val.filter(x => x.active)
                          .map(x => x.value)
                          .reduce((a, b) => a + b, 0);
  return `Total: ${result}`;
})

// ✅ Better: Compute outside, use in arrow function
const activeTotal = () => {
  return items.val
    .filter(x => x.active)
    .map(x => x.value)
    .reduce((a, b) => a + b, 0);
};
div(() => `Total: ${activeTotal()}`)

// Bundle Size Tips:
// - Import only components you use
// - Use native browser APIs when possible
// - Avoid large dependencies
// - VanJS itself is only 1.5KB!

// Memory Management:
// - Don't create new van.state() inside render functions
// - Clean up event listeners if added manually
// - VanJS handles DOM cleanup automatically
✅Best Practice: Keep reactive functions simple! Complex computations should be in separate derived state functions.
⚠️Warning: Never create van.state() inside arrow functions - creates new state on every render!

🎯 Advanced Topics Checklist

  • ✅File structure = Routes
  • ✅Services for API calls
  • ✅State as local as possible
  • ✅VanX only if needed
  • ✅Simple reactive functions
  • ✅Import only what you use

🎉 Congratulations!

You've completed the vwftutorial! You now know:

  • ✅VanJS basics and tag functions
  • ✅State management and reactivity
  • ✅Component library usage
  • ✅Routing and application structure
  • ✅Best practices and performance

Explore vwf

Discover what vwf can do