Core Framework APIs
Core utilities for routing, state management, validation, and component composition
🧭 File-Based Router V2
Zero-config routing with automatic route discovery, nested layouts, and lifecycle hooks - similar to Next.js App Router.
Core Concepts
- • Routes discovered from file structure automatically
- • page.js files define routes (not default exports)
- • layout.js files define nested layouts
- • Directory names become URL segments
- • No manual route configuration needed
Navigation API
import { router } from './utils/fileBasedRouterV2.js';
// Basic navigation
router.navigate('/dashboard');
// Navigate with state
router.navigate('/profile', { userId: 123 });
// Navigate with direction hint for transitions
router.navigate('/auth/email', null, 'forward');
// Go back
router.back();
// Get current path
const path = router.currentPath();
// Check if path matches
if (router.matches('/dashboard')) {
// Currently on dashboard
}File Structure Convention
src/app/
├── layout.js # Root layout (wraps all pages)
├── page.js # Home page (/)
├── dashboard/
│ └── page.js # Dashboard (/dashboard)
├── profile/
│ ├── page.js # Profile (/profile)
│ └── edit/
│ └── page.js # Profile edit (/profile/edit)
└── auth/
├── layout.js # Auth layout
├── page.js # Auth landing (/auth)
└── email/
└── page.js # Email entry (/auth/email)Page Component Pattern
// src/app/dashboard/page.js
import van from 'vanjs-core';
const { div, h1 } = van.tags;
// IMPORTANT: export const component (not default!)
export const component = () => {
return div({ class: 'dashboard-page' },
h1('Dashboard'),
// Page content
);
};
// Optional: Lifecycle hooks
component.postMount = () => {
console.log('Dashboard mounted');
// Initialize maps, charts, fetch data
};
component.preUnmount = () => {
console.log('Dashboard unmounting');
// Cleanup timers, listeners, resources
};
// Optional: Route metadata (future)
export const metadata = {
title: 'Dashboard',
requiresAuth: true
};Layout Component Pattern
// src/app/auth/layout.js
import van from 'vanjs-core';
import { AuthCard } from '../../shared-components/layout/AuthCard.js';
const { div } = van.tags;
// Layout receives outlet prop
export const component = ({ outlet }) => {
return div(
{ class: 'auth-layout min-h-screen' },
AuthCard({},
outlet // Child pages render here
)
);
};Benefits
- • Zero configuration - routes auto-discovered
- • Better organization - related files together
- • Nested layouts - reduce code duplication
- • Smart diffing - minimal DOM updates
- • Familiar pattern - like Next.js App Router
Features
- • Automatic route discovery
- • Nested layouts support
- • Lifecycle hooks (postMount, preUnmount)
- • Fixed outlet system for smooth transitions
- • State preservation in layouts
✓ Form Validation (validator.js)
Comprehensive validation library for forms with 80+ validators for email, phone, URL, password strength, and more.
Common Validators
| Validator | Description | Example |
|---|---|---|
| isEmail() | Validate email address | user@example.com |
| isMobilePhone() | Validate phone number | +1-555-123-4567 |
| isURL() | Validate web address | https://example.com |
| isLength() | Check string length | min: 8, max: 20 |
| isStrongPassword() | Password strength | minLength, minSymbols |
| isAlphanumeric() | Letters and numbers only | username123 |
| isNumeric() | Numbers only | 12345 |
| matches() | Regex pattern match | /^[A-Z]/ |
Basic Usage
import validator from 'validator';
// Email validation
const isValid = validator.isEmail('user@example.com');
// → true
// Phone validation with locale
validator.isMobilePhone('+1-555-123-4567', 'en-US');
// → true
// URL validation
validator.isURL('https://example.com');
// → true
// Length validation
validator.isLength('password123', { min: 8, max: 20 });
// → true
// Password strength
validator.isStrongPassword('Abc123!@#', {
minLength: 8,
minLowercase: 1,
minUppercase: 1,
minNumbers: 1,
minSymbols: 1
});
// → trueReal-Time Validation Pattern
import validator from 'validator';
const email = van.state('');
const emailMsg = van.state({});
const validateEmail = (value) => {
if (!value) {
return { code: 'error', detail: 'Email is required' };
}
if (!validator.isEmail(value)) {
return { code: 'error', detail: 'Invalid email address' };
}
return { code: 'success', detail: 'Email valid!' };
};
// In your component
Input({
label: 'Email',
type: 'email',
value: email,
message: emailMsg,
placeholder: 'you@example.com',
oninput: (e) => {
email.val = e.target.value;
if (e.target.value) {
emailMsg.val = validateEmail(e.target.value);
}
}
})Multi-Field Validation
const validatePassword = (value) => {
if (!value) {
return { code: 'error', detail: 'Password is required' };
}
if (!validator.isLength(value, { min: 8 })) {
return { code: 'warning', detail: 'Minimum 8 characters' };
}
if (!/[A-Z]/.test(value)) {
return { code: 'warning', detail: 'Add uppercase letter' };
}
if (!/[0-9]/.test(value)) {
return { code: 'warning', detail: 'Add number' };
}
return { code: 'success', detail: 'Strong password!' };
};
const handleSubmit = async (e) => {
e.preventDefault();
// Validate all fields
emailMsg.val = validateEmail(email.val);
passwordMsg.val = validatePassword(password.val);
// Check if all valid
if (emailMsg.val.code !== 'success' ||
passwordMsg.val.code !== 'success') {
return;
}
// Submit form
await submitForm({ email: email.val, password: password.val });
};More Validators Available
isJSON()isDate()isCreditCard()isIP()isPostalCode()isHexColor()isBase64()isJWT()isBefore()isAfter()isEmpty()contains()
80+ validators available - see validator.js documentation
✨ Best Practices
Router Best Practices
- • Keep pages focused - logic in components
- • Use layouts for shared UI (headers, footers)
- • Implement lifecycle hooks for cleanup
- • Follow naming: page.js and layout.js
- • Organize by feature in directories
Validation Best Practices
- • Validate on input for real-time feedback
- • Validate on submit for final check
- • Provide clear error messages
- • Use warning for suggestions, error for blocks
- • Show success states for confidence
Performance Tips
- • Debounce expensive validations
- • Cache validation results when possible
- • Clean up resources in preUnmount
- • Use lifecycle hooks appropriately
- • Avoid validation in tight render loops
Common Patterns
- • Create reusable validator functions
- • Centralize validation logic
- • Use message states for feedback
- • Implement progressive validation
- • Handle async validation gracefully
🔗 Related Resources
Explore vwf
Discover what vwf can do