# Pages API ๐Ÿ“„ Pages in SigPro are special components designed for route-based navigation with **automatic cleanup**. When you navigate away from a page, all signals, effects, and event listeners created within that page are automatically cleaned up - no memory leaks, no manual cleanup needed. ## `$.page(setupFunction)` Creates a page with automatic cleanup of all signals and effects when navigated away. ```javascript import { $, html } from 'sigpro'; export default $.page(() => { // All signals and effects created here // will be automatically cleaned up on navigation const count = $(0); $.effect(() => { console.log(`Count: ${count()}`); }); return html`

My Page

Count: ${count}

`; }); ``` ## ๐Ÿ“‹ API Reference | Parameter | Type | Description | |-----------|------|-------------| | `setupFunction` | `Function` | Function that returns the page content. Receives context object with `params` and `onUnmount` | ### Context Object Properties | Property | Type | Description | |----------|------|-------------| | `params` | `Object` | Route parameters passed to the page | | `onUnmount` | `Function` | Register cleanup callbacks (alternative to automatic cleanup) | ## ๐ŸŽฏ Basic Usage ### Simple Page ```javascript // pages/home.js import { $, html } from 'sigpro'; export default $.page(() => { const title = $('Welcome to SigPro'); return html`

${title}

This page will clean itself up when you navigate away.

`; }); ``` ### Page with Route Parameters ```javascript // pages/user.js import { $, html } from 'sigpro'; export default $.page(({ params }) => { // Access route parameters const userId = params.id; const userData = $(null); const loading = $(false); // Auto-cleaned effect $.effect(() => { loading(true); $.fetch(`/api/users/${userId}`, null, loading) .then(data => userData(data)); }); return html`
${() => loading() ? html`
Loading...
` : html`

User Profile: ${userData()?.name}

Email: ${userData()?.email}

`}
`; }); ``` ## ๐Ÿงน Automatic Cleanup The magic of `$.page` is automatic cleanup. Everything created inside the page is tracked and cleaned up: ```javascript export default $.page(() => { // โœ… Signals are auto-cleaned const count = $(0); const user = $(null); // โœ… Effects are auto-cleaned $.effect(() => { document.title = `Count: ${count()}`; }); // โœ… Event listeners are auto-cleaned window.addEventListener('resize', handleResize); // โœ… Intervals and timeouts are auto-cleaned const interval = setInterval(() => { refreshData(); }, 5000); return html`
Page content
`; }); // When navigating away: all signals, effects, listeners, intervals STOP ``` ## ๐Ÿ“ Manual Cleanup with `onUnmount` Sometimes you need custom cleanup logic. Use `onUnmount` for that: ```javascript export default $.page(({ onUnmount }) => { // WebSocket connection const socket = new WebSocket('wss://api.example.com'); socket.onmessage = (event) => { updateData(JSON.parse(event.data)); }; // Manual cleanup onUnmount(() => { socket.close(); console.log('WebSocket closed'); }); return html`
Real-time updates
`; }); ``` ## ๐Ÿ”„ Integration with Router Pages are designed to work seamlessly with `$.router`: ```javascript import { $, html } from 'sigpro'; import HomePage from './pages/Home.js'; import UserPage from './pages/User.js'; import SettingsPage from './pages/Settings.js'; const routes = [ { path: '/', component: HomePage }, { path: '/user/:id', component: UserPage }, { path: '/settings', component: SettingsPage }, ]; // Mount router document.body.appendChild($.router(routes)); ``` ## ๐Ÿ’ก Practical Examples ### Example 1: Data Fetching Page ```javascript // pages/posts.js export default $.page(({ params }) => { const posts = $([]); const loading = $(true); const error = $(null); $.effect(() => { fetch('/api/posts') .then(res => res.json()) .then(data => { posts(data); loading(false); }) .catch(err => { error(err.message); loading(false); }); }); return html`

Blog Posts

${() => loading() ? html`
Loading posts...
` : error() ? html`
Error: ${error()}
` : html`
${posts().map(post => html` `)}
`}
`; }); ``` ### Example 2: Real-time Dashboard ```javascript // pages/dashboard.js export default $.page(({ onUnmount }) => { const metrics = $({ cpu: 0, memory: 0, requests: 0 }); // Auto-refresh data const refreshInterval = setInterval(async () => { const data = await $.fetch('/api/metrics'); if (data) metrics(data); }, 5000); // Manual cleanup for interval onUnmount(() => clearInterval(refreshInterval)); // Live clock const currentTime = $(new Date()); const clockInterval = setInterval(() => { currentTime(new Date()); }, 1000); onUnmount(() => clearInterval(clockInterval)); return html`

System Dashboard

Last updated: ${() => currentTime().toLocaleTimeString()}

CPU Usage

${() => metrics().cpu}%

Memory Usage

${() => metrics().memory}%

Requests/min

${() => metrics().requests}

`; }); ``` ### Example 3: Multi-step Form ```javascript // pages/checkout.js export default $.page(({ onUnmount }) => { const step = $(1); const formData = $({ email: '', address: '', payment: '' }); // Warn user before leaving const handleBeforeUnload = (e) => { if (step() < 3) { e.preventDefault(); e.returnValue = ''; } }; window.addEventListener('beforeunload', handleBeforeUnload); onUnmount(() => { window.removeEventListener('beforeunload', handleBeforeUnload); }); const nextStep = () => step(s => Math.min(s + 1, 3)); const prevStep = () => step(s => Math.max(s - 1, 1)); return html`

Checkout - Step ${step} of 3

${() => { switch(step()) { case 1: return html`

Email

formData().email} @input=${(e) => formData({...formData(), email: e.target.value})} />
`; case 2: return html`

Address

`; case 3: return html`

Payment

formData().payment} @input=${(e) => formData({...formData(), payment: e.target.value})} />
`; } }}
${() => step() > 1 ? html` ` : ''} ${() => step() < 3 ? html` ` : html` `}
`; }); ``` ### Example 4: Page with Tabs ```javascript // pages/profile.js export default $.page(({ params }) => { const activeTab = $('overview'); const userData = $(null); // Load user data $.effect(() => { $.fetch(`/api/users/${params.id}`) .then(data => userData(data)); }); const tabs = { overview: () => html`

Overview

Username: ${userData()?.username}

Member since: ${userData()?.joined}

`, posts: () => html`

Posts

${userData()?.posts.map(post => html`
${post.title}
`)}
`, settings: () => html`

Settings

` }; return html`

${() => userData()?.name}

${Object.keys(tabs).map(tab => html` `)}
${() => tabs[activeTab()]()}
`; }); ``` ## ๐ŸŽฏ Advanced Patterns ### Page with Nested Routes ```javascript // pages/settings/index.js export default $.page(({ params }) => { const section = params.section || 'general'; const sections = { general: () => import('./general.js').then(m => m.default), security: () => import('./security.js').then(m => m.default), notifications: () => import('./notifications.js').then(m => m.default) }; const currentSection = $(null); $.effect(() => { sections[section]().then(comp => currentSection(comp)); }); return html`
${currentSection}
`; }); ``` ### Page with Authentication ```javascript // pages/dashboard.js export default $.page(({ onUnmount }) => { const isAuthenticated = $(false); const authCheck = $.effect(() => { const token = localStorage.getItem('token'); isAuthenticated(!!token); }); // Redirect if not authenticated $.effect(() => { if (!isAuthenticated()) { $.router.go('/login'); } }); return html`

Protected Dashboard

`; }); ``` ## ๐Ÿ“Š Summary | Feature | Description | |---------|-------------| | **Automatic Cleanup** | All signals, effects, and resources auto-cleaned on navigation | | **Memory Safe** | No memory leaks, even with complex nested effects | | **Router Integration** | Designed to work perfectly with `$.router` | | **Parameters** | Access route parameters via `params` object | | **Manual Cleanup** | `onUnmount` for custom cleanup needs | | **Zero Configuration** | Just wrap your page in `$.page()` and it works | --- > **Pro Tip:** Always wrap route-based views in `$.page()` to ensure proper cleanup. This prevents memory leaks and ensures your app stays performant even after many navigation changes.