Homepage.dev Module SDK
Complete developer guide for building extensible homepage modules with real-time collaboration support
🎯 SDK Overview
The Homepage.dev Module SDK enables developers to create rich, interactive modules that integrate seamlessly with the real-time collaborative homepage platform. Build everything from simple widgets to complex productivity tools with full TypeScript support and comprehensive APIs.
📖 Related Documentation:
- Architecture Overview - Technical platform architecture
- Product Requirements - Platform vision and module ecosystem
- Implementation Plan - Real-time features and integration
What You Can Build
- Productivity Tools: Task managers, calendars, note-taking apps
- Data Dashboards: Stock tickers, analytics widgets, system monitors
- Communication: Chat interfaces, notification centers, team updates
- Content: RSS readers, social feeds, bookmark managers
- External Integrations: Third-party service connectors, API dashboards
- Custom Utilities: Timers, calculators, weather widgets, and more
🚀 Quick Start
Installation
# Create new module project
npx @homepage/create-module my-awesome-module
cd my-awesome-module
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
# Publish to marketplace
npm run publishBasic Module Structure
// src/index.ts
import { HomepageModule, ModuleConfig } from '@homepage/sdk'
interface MyModuleConfig {
title: string
refreshInterval: number
apiKey?: string
}
const myModule: HomepageModule<MyModuleConfig> = {
// Module metadata
metadata: {
id: 'my-awesome-module',
name: 'My Awesome Module',
description: 'An amazing module that does incredible things',
version: '1.0.0',
author: {
name: 'Your Name',
email: 'you@example.com',
website: 'https://yoursite.com'
},
category: 'productivity',
tags: ['productivity', 'automation', 'tools'],
icon: 'https://cdn.example.com/icon.svg'
},
// React component
component: MyModuleComponent,
// Configuration schema
configSchema: {
type: 'object',
properties: {
title: { type: 'string', default: 'My Module' },
refreshInterval: { type: 'number', default: 30000, minimum: 5000 },
apiKey: { type: 'string', description: 'Optional API key' }
},
required: ['title', 'refreshInterval']
},
// Default configuration
defaultConfig: {
title: 'My Awesome Module',
refreshInterval: 30000
},
// Module permissions
permissions: {
required: ['storage:local'],
optional: ['network:external'],
network: {
domains: ['api.example.com'],
methods: ['GET', 'POST']
}
},
// Lifecycle hooks
lifecycle: {
onMount: async (context) => {
console.log('Module mounted', context)
},
onUnmount: async () => {
console.log('Module unmounted')
}
}
}
export default myModule🧩 Module Interface Specification
Core Module Interface
interface HomepageModule<TConfig = any> {
// Identity and metadata
metadata: ModuleMetadata
// React component for rendering
component: React.ComponentType<ModuleProps<TConfig>>
// Configuration schema (JSON Schema)
configSchema: JSONSchema7
defaultConfig: TConfig
// Permissions and security
permissions: ModulePermissions
// Lifecycle management
lifecycle: ModuleLifecycle
// Optional data handlers
dataHandlers?: ModuleDataHandlers<TConfig>
}
interface ModuleMetadata {
id: string
name: string
description: string
longDescription?: string
version: string
author: {
name: string
email?: string
website?: string
}
// Categorization
category: ModuleCategory
tags: string[]
keywords: string[]
// Visual assets
icon: string
screenshots: string[]
bannerImage?: string
// Display configuration
defaultSize: { width: number; height: number }
minSize: { width: number; height: number }
maxSize: { width: number; height: number }
resizable: boolean
// Behavior settings
refreshInterval?: number
singleton?: boolean // Only one instance allowed
requiresAuth?: boolean
// Compatibility
minHomepageVersion: string
maxHomepageVersion?: string
dependencies: string[]
}
type ModuleCategory =
| 'productivity' | 'communication' | 'entertainment'
| 'utilities' | 'data' | 'social' | 'education'
| 'business' | 'developer' | 'custom'Module Props and Context
interface ModuleProps<TConfig = any> {
// Module configuration
config: TConfig
onConfigChange: (newConfig: Partial<TConfig>) => void
// Module context
context: ModuleContext
// Real-time collaboration
collaboration: CollaborationContext
// Homepage integration
homepage: HomepageContext
// Utilities and APIs
api: ModuleAPI
storage: StorageAPI
messaging: MessagingAPI
}
interface ModuleContext {
// Module instance info
instanceId: string
moduleId: string
version: string
// User and organization
user: {
id: string
name: string
email: string
avatar?: string
}
organization?: {
id: string
name: string
slug: string
}
// Environment
environment: 'development' | 'production'
isPreview: boolean
// Permissions granted
permissions: string[]
// Theme and styling
theme: {
mode: 'light' | 'dark'
primaryColor: string
accentColor: string
}
}🔄 Real-time Collaboration APIs
Collaborative State Management
interface CollaborationContext {
// Current collaborators
activeUsers: CollaboratorInfo[]
// Shared state management
sharedState: SharedStateAPI
// Real-time events
events: EventAPI
// Conflict resolution
conflicts: ConflictAPI
}
interface CollaboratorInfo {
userId: string
userName: string
userAvatar?: string
cursor?: { x: number; y: number }
isActive: boolean
lastSeen: number
}
// Shared state between module instances
interface SharedStateAPI {
// Get shared value
get<T>(key: string): T | undefined
// Set shared value with automatic sync
set<T>(key: string, value: T): void
// Subscribe to changes
subscribe<T>(key: string, callback: (value: T) => void): () => void
// Delete shared value
delete(key: string): void
// Get all keys
keys(): string[]
// Batch operations
batch(operations: SharedStateOperation[]): void
}
// Real-time events between modules
interface EventAPI {
// Emit event to other modules
emit(eventType: string, payload: any): void
// Listen for events
on(eventType: string, callback: (payload: any) => void): () => void
// One-time event listener
once(eventType: string, callback: (payload: any) => void): void
// Remove event listener
off(eventType: string, callback?: Function): void
}Inter-Module Communication
interface MessagingAPI {
// Send message to specific module instance
sendMessage(targetInstanceId: string, message: ModuleMessage): void
// Broadcast message to all instances of a module type
broadcast(moduleId: string, message: ModuleMessage): void
// Listen for incoming messages
onMessage(callback: (message: ModuleMessage) => void): () => void
// Request-response pattern
request<T>(targetInstanceId: string, request: any): Promise<T>
// Handle incoming requests
onRequest<T>(handler: (request: any) => T | Promise<T>): () => void
}
interface ModuleMessage {
id: string
type: string
payload: any
timestamp: number
fromInstanceId: string
toInstanceId?: string
}💾 Data Storage and Persistence
Storage API
interface StorageAPI {
// Local storage (per module instance)
local: LocalStorageAPI
// Shared storage (across module instances)
shared: SharedStorageAPI
// User storage (per user, across all instances)
user: UserStorageAPI
// File storage for larger data
files: FileStorageAPI
}
interface LocalStorageAPI {
// Simple key-value storage
get<T>(key: string): Promise<T | null>
set<T>(key: string, value: T): Promise<void>
delete(key: string): Promise<void>
clear(): Promise<void>
keys(): Promise<string[]>
// Structured data with queries
query<T>(filter: QueryFilter): Promise<T[]>
index<T>(indexName: string, data: T[]): Promise<void>
}
interface FileStorageAPI {
// Upload files
upload(file: File): Promise<FileInfo>
uploadFromUrl(url: string): Promise<FileInfo>
// Download files
download(fileId: string): Promise<Blob>
getUrl(fileId: string): Promise<string>
// File management
delete(fileId: string): Promise<void>
list(): Promise<FileInfo[]>
// File sharing
share(fileId: string, permissions: SharePermissions): Promise<string>
}🌐 External APIs and Network Access
Network API
interface NetworkAPI {
// HTTP requests (within permitted domains)
fetch(url: string, options?: RequestOptions): Promise<Response>
// WebSocket connections
websocket(url: string, protocols?: string[]): Promise<WebSocket>
// Rate limiting info
getRateLimit(): Promise<RateLimitInfo>
// Proxy for CORS issues
proxy(url: string): Promise<string>
}
// Controlled external access
interface ExternalAPIAccess {
// Pre-configured popular APIs
weather: WeatherAPI
stocks: StocksAPI
news: NewsAPI
calendar: CalendarAPI
// OAuth integration
oauth: OAuthAPI
// Webhook handling
webhooks: WebhookAPI
}
interface OAuthAPI {
// Initiate OAuth flow
authorize(provider: OAuthProvider, scopes: string[]): Promise<OAuthToken>
// Refresh tokens
refresh(provider: OAuthProvider): Promise<OAuthToken>
// Get current token
getToken(provider: OAuthProvider): Promise<OAuthToken | null>
// Revoke access
revoke(provider: OAuthProvider): Promise<void>
}
type OAuthProvider = 'google' | 'github' | 'microsoft' | 'slack' | 'discord'🎨 UI Components and Styling
Module UI Framework
// Pre-built UI components
import {
Button, Input, Card, Modal, Toast,
Chart, DataTable, Timeline, Calendar,
Avatar, Badge, Progress, Spinner
} from '@homepage/ui'
// Styling utilities
import {
useTheme, useResponsive, useAnimation,
cn, styled, css
} from '@homepage/ui/styling'
// Example component
function MyModuleComponent({ config, context, api }: ModuleProps) {
const theme = useTheme()
const [data, setData] = useState([])
const [loading, setLoading] = useState(false)
// Real-time data subscription
useEffect(() => {
const unsubscribe = api.storage.shared.subscribe('live-data', setData)
return unsubscribe
}, [])
return (
<Card className="p-4 h-full">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold">{config.title}</h3>
<Button
size="sm"
onClick={refreshData}
disabled={loading}
>
{loading ? <Spinner size="sm" /> : 'Refresh'}
</Button>
</div>
<DataTable
data={data}
columns={columns}
pagination
className="flex-1"
/>
</Card>
)
}Responsive Design
// Responsive utilities
import { useResponsive, breakpoints } from '@homepage/ui'
function ResponsiveModule({ config }: ModuleProps) {
const { isMobile, isTablet, isDesktop } = useResponsive()
return (
<div className={cn(
"p-4",
isMobile && "p-2",
isTablet && "p-3",
isDesktop && "p-4"
)}>
{isMobile ? (
<MobileLayout data={data} />
) : (
<DesktopLayout data={data} />
)}
</div>
)
}
// CSS-in-JS with theme support
const StyledContainer = styled.div`
background: ${props => props.theme.colors.background};
border: 1px solid ${props => props.theme.colors.border};
border-radius: ${props => props.theme.radii.md};
@media (max-width: ${breakpoints.md}) {
padding: 0.5rem;
}
`🔧 Development Tools and CLI
Module Development CLI
# Create new module project
npx @homepage/create-module <module-name>
# Development commands
npm run dev # Start development server with hot reload
npm run build # Build module for production
npm run test # Run module tests
npm run lint # Lint module code
npm run type-check # TypeScript type checking
# Publishing commands
npm run validate # Validate module before publishing
npm run publish # Publish to module marketplace
npm run unpublish # Remove from marketplace
# Marketplace commands
npm run analytics # View module analytics
npm run reviews # Manage module reviews
npm run revenue # View revenue reportsDevelopment Server Features
interface DevServer {
// Hot reload with state preservation
hotReload: {
preserveState: boolean
reloadOnConfigChange: boolean
reloadOnDependencyChange: boolean
}
// Mock APIs for testing
mocking: {
externalAPIs: boolean
storage: boolean
collaboration: boolean
authentication: boolean
}
// Performance profiling
profiling: {
renderTime: boolean
memoryUsage: boolean
networkRequests: boolean
realtimeLatency: boolean
}
// Debugging tools
debugging: {
stateInspector: boolean
eventLogger: boolean
errorBoundary: boolean
accessibilityChecker: boolean
}
}🧪 Testing Framework
Module Testing Utilities
import {
renderModule, mockModuleContext, mockCollaboration,
fireEvent, waitFor, screen
} from '@homepage/testing'
describe('MyModule', () => {
test('renders with default config', async () => {
const { container } = await renderModule(MyModule, {
config: { title: 'Test Module' },
context: mockModuleContext()
})
expect(screen.getByText('Test Module')).toBeInTheDocument()
})
test('handles real-time updates', async () => {
const collaboration = mockCollaboration()
const { rerender } = await renderModule(MyModule, {
collaboration
})
// Simulate real-time update
collaboration.sharedState.set('data', [{ id: 1, name: 'Item 1' }])
await waitFor(() => {
expect(screen.getByText('Item 1')).toBeInTheDocument()
})
})
test('handles external API calls', async () => {
const mockAPI = jest.fn().mockResolvedValue({ data: [] })
await renderModule(MyModule, {
api: {
network: { fetch: mockAPI }
}
})
fireEvent.click(screen.getByRole('button', { name: /refresh/i }))
expect(mockAPI).toHaveBeenCalledWith('https://api.example.com/data')
})
})Performance Testing
import { benchmarkModule, profileMemory, measureLatency } from '@homepage/testing'
test('performance benchmarks', async () => {
const results = await benchmarkModule(MyModule, {
iterations: 100,
scenarios: ['mount', 'update', 'unmount']
})
expect(results.mount.average).toBeLessThan(100) // < 100ms
expect(results.memory.peak).toBeLessThan(50 * 1024 * 1024) // < 50MB
})📊 Analytics and Monitoring
Module Analytics API
interface ModuleAnalytics {
// Track user interactions
trackEvent(eventName: string, properties?: Record<string, any>): void
// Track performance metrics
trackPerformance(metric: PerformanceMetric): void
// Track errors
trackError(error: Error, context?: Record<string, any>): void
// Custom metrics
trackMetric(name: string, value: number, unit: string): void
// User journey tracking
trackPageView(page: string): void
trackUserAction(action: string, target: string): void
}
// Usage in module
function MyModule({ api }: ModuleProps) {
const handleButtonClick = () => {
api.analytics.trackEvent('button_clicked', {
button: 'refresh',
config: config.title
})
refreshData()
}
useEffect(() => {
api.analytics.trackPageView('module_mounted')
}, [])
}Revenue Tracking
interface RevenueAPI {
// Track subscription events
trackSubscription(event: 'started' | 'renewed' | 'cancelled'): void
// Track usage for freemium models
trackUsage(feature: string, count: number): void
// In-app purchases
trackPurchase(item: string, amount: number, currency: string): void
// Conversion funnel
trackConversion(step: string, success: boolean): void
}🛡️ Security and Permissions
Permission System
interface ModulePermissions {
// Required permissions (user must grant)
required: Permission[]
// Optional permissions (module works without)
optional: Permission[]
// Network access
network?: {
domains: string[] // Allowed domains
methods: ('GET' | 'POST' | 'PUT' | 'DELETE')[]
rateLimit?: number // Requests per minute
}
// Storage access
storage?: {
quota: number // Storage quota in MB
types: ('local' | 'shared' | 'user')[]
}
// Device permissions
device?: {
camera?: boolean
microphone?: boolean
geolocation?: boolean
notifications?: boolean
}
// API access
apis?: {
external: string[] // External API access
internal: string[] // Homepage API access
}
}
type Permission =
| 'storage:local' | 'storage:shared' | 'storage:user'
| 'network:external' | 'network:internal'
| 'device:camera' | 'device:microphone' | 'device:geolocation'
| 'notifications:show' | 'notifications:push'
| 'collaboration:read' | 'collaboration:write'
| 'analytics:basic' | 'analytics:detailed'Security Best Practices
// Input validation
import { validateConfig, sanitizeInput, escapeHTML } from '@homepage/security'
function MyModule({ config }: ModuleProps) {
// Always validate configuration
const validatedConfig = validateConfig(config, configSchema)
// Sanitize user inputs
const handleUserInput = (input: string) => {
const sanitized = sanitizeInput(input)
const safe = escapeHTML(sanitized)
return safe
}
// Secure API calls
const fetchData = async () => {
try {
const response = await api.network.fetch('/api/data', {
headers: {
'Authorization': `Bearer ${await api.oauth.getToken('provider')}`
}
})
return response.json()
} catch (error) {
api.analytics.trackError(error)
throw error
}
}
}💰 Monetization and Marketplace
Publishing to Marketplace
// module.config.ts
export const moduleConfig = {
// Basic info
metadata: {
name: 'My Awesome Module',
description: 'Brief description',
category: 'productivity',
tags: ['productivity', 'tools']
},
// Pricing model
pricing: {
model: 'freemium', // 'free' | 'paid' | 'freemium'
price: 499, // Price in cents ($4.99)
currency: 'USD',
trialDays: 14,
subscriptionType: 'monthly' // 'monthly' | 'yearly'
},
// Revenue sharing (70% developer, 30% platform)
revenueShare: {
developer: 70,
platform: 30
}
}Freemium Features
function MyModule({ context, api }: ModuleProps) {
const { subscription } = context.user
const isPro = subscription?.tier === 'pro'
// Feature gating
const handleAdvancedFeature = () => {
if (!isPro) {
api.ui.showUpgradeModal({
feature: 'Advanced Analytics',
price: '$4.99/month'
})
return
}
// Pro feature implementation
showAdvancedAnalytics()
}
return (
<div>
<BasicFeatures />
{isPro ? (
<AdvancedFeatures />
) : (
<UpgradePrompt feature="Advanced Features" />
)}
</div>
)
}📚 Example Modules
Stock Ticker Module
import { HomepageModule } from '@homepage/sdk'
import { LineChart, Card } from '@homepage/ui'
interface StockConfig {
symbols: string[]
refreshInterval: number
showChart: boolean
}
const StockTicker: HomepageModule<StockConfig> = {
metadata: {
id: 'stock-ticker',
name: 'Stock Ticker',
description: 'Real-time stock price tracking',
category: 'data',
defaultSize: { width: 4, height: 2 }
},
component: function StockTickerComponent({ config, api }) {
const [stocks, setStocks] = useState([])
const [loading, setLoading] = useState(false)
// Real-time stock updates
useEffect(() => {
const fetchStocks = async () => {
setLoading(true)
try {
const data = await api.network.fetch(
`https://api.stockprice.com/quotes?symbols=${config.symbols.join(',')}`
)
const quotes = await data.json()
setStocks(quotes)
// Share with other instances
api.collaboration.sharedState.set('stocks', quotes)
} catch (error) {
api.analytics.trackError(error)
} finally {
setLoading(false)
}
}
fetchStocks()
const interval = setInterval(fetchStocks, config.refreshInterval)
return () => clearInterval(interval)
}, [config.symbols, config.refreshInterval])
return (
<Card className="p-4 h-full">
<h3 className="text-lg font-semibold mb-4">Stock Prices</h3>
<div className="space-y-2">
{stocks.map(stock => (
<div key={stock.symbol} className="flex justify-between">
<span className="font-medium">{stock.symbol}</span>
<span className={`font-mono ${
stock.change >= 0 ? 'text-green-600' : 'text-red-600'
}`}>
${stock.price} ({stock.change > 0 ? '+' : ''}{stock.change}%)
</span>
</div>
))}
</div>
{config.showChart && (
<LineChart
data={stocks}
xKey="timestamp"
yKey="price"
className="mt-4 h-32"
/>
)}
{loading && <div className="absolute inset-0 bg-white/80 flex items-center justify-center">Loading...</div>}
</Card>
)
},
configSchema: {
type: 'object',
properties: {
symbols: {
type: 'array',
items: { type: 'string' },
default: ['AAPL', 'GOOGL', 'MSFT']
},
refreshInterval: {
type: 'number',
default: 30000,
minimum: 5000
},
showChart: {
type: 'boolean',
default: true
}
}
},
permissions: {
required: ['network:external'],
network: {
domains: ['api.stockprice.com'],
methods: ['GET']
}
}
}
export default StockTickerTeam Chat Module
const TeamChat: HomepageModule = {
metadata: {
id: 'team-chat',
name: 'Team Chat',
description: 'Real-time team communication',
category: 'communication',
defaultSize: { width: 4, height: 3 }
},
component: function TeamChatComponent({ api, context, collaboration }) {
const [messages, setMessages] = useState([])
const [newMessage, setNewMessage] = useState('')
// Listen for new messages
useEffect(() => {
const unsubscribe = collaboration.events.on('message', (message) => {
setMessages(prev => [...prev, message])
})
return unsubscribe
}, [])
const sendMessage = () => {
if (!newMessage.trim()) return
const message = {
id: crypto.randomUUID(),
text: newMessage,
userId: context.user.id,
userName: context.user.name,
timestamp: Date.now()
}
// Broadcast to all team chat instances
collaboration.events.emit('message', message)
setNewMessage('')
}
return (
<Card className="p-4 h-full flex flex-col">
<h3 className="text-lg font-semibold mb-4">Team Chat</h3>
<div className="flex-1 overflow-y-auto space-y-2 mb-4">
{messages.map(message => (
<div key={message.id} className="flex space-x-2">
<Avatar size="sm" src={message.userAvatar} />
<div>
<div className="text-sm font-medium">{message.userName}</div>
<div className="text-sm text-gray-600">{message.text}</div>
</div>
</div>
))}
</div>
<div className="flex space-x-2">
<Input
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="Type a message..."
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
className="flex-1"
/>
<Button onClick={sendMessage}>Send</Button>
</div>
</Card>
)
},
permissions: {
required: ['collaboration:write']
}
}🚀 Publishing Your Module
Publishing Checklist
- Module follows naming conventions
- All required permissions declared
- Configuration schema is valid
- Module has proper error handling
- Analytics tracking implemented
- Security best practices followed
- Module tested across different screen sizes
- Documentation and screenshots provided
- Privacy policy and terms (if applicable)
Review Process
- Automated Testing: Security scan, performance test, compatibility check
- Manual Review: Code quality, user experience, marketplace guidelines
- Approval Timeline: 2-5 business days for new modules
- Updates: Fast-track approval for minor updates
Developer Success Program
- Technical Support: Dedicated developer support channel
- Marketing Support: Featured placement for high-quality modules
- Revenue Optimization: Analytics and conversion optimization guidance
- Community: Developer forums, hackathons, and events
📞 Support and Resources
Documentation
- API Reference: Complete TypeScript definitions
- Tutorials: Step-by-step module building guides
- Examples: Open-source example modules
- Best Practices: Performance, security, and UX guidelines
Developer Support
- Discord Community: Real-time developer chat
- GitHub Issues: Bug reports and feature requests
- Office Hours: Weekly developer Q&A sessions
- 1:1 Support: Premium support for verified developers
Useful Links
- SDK Repository: https://github.com/homepage/sdk
- Example Modules: https://github.com/homepage/examples
- Developer Portal: https://developers.homepage.dev
- Marketplace: https://homepage.dev/marketplace
Build amazing modules that enhance productivity and bring teams together. The Homepage.dev SDK provides everything you need to create powerful, real-time collaborative experiences.
📖 Continue Your Journey:
- Implementation Checklist - Development roadmap
- Standalone Strategy - Platform business strategy
- Development Tenets - Coding standards and principles
Happy building! 🚀
Last Updated: January 2025