Other Utilities
Chrome Extension Starter includes several additional utility modules that simplify common tasks in extension development.
DOM Utilities
Location: src/shared/lib/dom.ts
Helper functions for safe DOM manipulation in content scripts.
mount(id?)
Create or retrieve a mount point for UI elements.
Parameters:
id?: string— Element ID (default:'ces-root')
Returns: HTMLElement
Usage:
import { mount } from '@/shared/lib/dom';
import { render } from 'preact';
// Create mount point
const el = mount('ces-overlay');
// Render UI
render(<MyComponent />, el);
How it works:
- Looks for existing element by ID
- Creates new div if not found
- Appends to document.body
- Returns the element
Example: Multiple mount points
const overlay = mount('ces-overlay');
const sidebar = mount('ces-sidebar');
render(<Overlay />, overlay);
render(<Sidebar />, sidebar);
Internationalization (i18n)
Location: src/shared/lib/i18n.ts
Utilities for multi-language support using Chrome's i18n API.
t(key, ...substitutions)
Get localized message from _locales/ directory.
Parameters:
key: string— Message key...substitutions: string[]— Optional placeholders
Returns: string
Usage:
import { t } from '@/shared/lib/i18n';
// Simple message
const title = t('extName');
// Message with substitution
const greeting = t('welcomeMessage', 'John');
Defining Messages
Create message files in public/_locales/{locale}/messages.json:
English (public/_locales/en/messages.json):
{
"extName": {
"message": "Chrome Extension Starter"
},
"extDescription": {
"message": "A modern Chrome extension template"
},
"welcomeMessage": {
"message": "Welcome, $USER$!",
"placeholders": {
"user": {
"content": "$1",
"example": "John"
}
}
},
"backgroundChanged": {
"message": "Background changed to $COLOR$",
"placeholders": {
"color": {
"content": "$1",
"example": "#0ea5e9"
}
}
}
}
Japanese (public/_locales/ja/messages.json):
{
"extName": {
"message": "Chrome拡張機能スターター"
},
"welcomeMessage": {
"message": "ようこそ、$USER$さん!",
"placeholders": {
"user": {
"content": "$1"
}
}
}
}
Usage in UI
import { t } from '@/shared/lib/i18n';
const Popup = () => {
return (
<div>
<h1>{t('extName')}</h1>
<p>{t('extDescription')}</p>
<p>{t('welcomeMessage', 'Alice')}</p>
</div>
);
};
Usage in Content Scripts
import { t } from '@/shared/lib/i18n';
bus.on(MSG.CHANGE_BG, (payload) => {
const message = t('backgroundChanged', payload.color);
showNotification(message);
return { ok: true };
});
Error Handling
Location: src/shared/lib/error.ts
Utilities for structured error handling.
toErrorResponse(error, code?, details?)
Convert errors to structured format for messaging.
Parameters:
error: Error | string— Error instance or messagecode?: string— Optional error codedetails?: unknown— Additional context
Returns: ErrorResponse
Usage:
import { toErrorResponse } from '@/shared/lib/error';
try {
await riskyOperation();
} catch (err) {
const errorResponse = toErrorResponse(err, 'OPERATION_FAILED');
console.error(errorResponse);
// {
// error: {
// message: 'Operation failed',
// code: 'OPERATION_FAILED',
// details: undefined
// }
// }
}
In Message Handlers:
bus.on(MSG.GET_USER, async (payload) => {
if (!payload.userId) {
throw toErrorResponse('User ID is required', 'INVALID_INPUT');
}
try {
const user = await fetchUser(payload.userId);
return user;
} catch (err) {
throw toErrorResponse(err, 'FETCH_FAILED', { userId: payload.userId });
}
});
Settings Management
Location: src/shared/lib/setting.ts
Utilities for managing extension settings with defaults and validation.
Features
- Default value support
- Type-safe settings schema
- Storage area selection (sync/local)
- Validation helpers
Example Implementation
// Define settings schema
interface Settings {
theme: 'light' | 'dark' | 'auto';
language: string;
notifications: boolean;
}
const DEFAULT_SETTINGS: Settings = {
theme: 'auto',
language: 'en',
notifications: true
};
// Get settings with defaults
export const getSettings = async (): Promise<Settings> => {
const stored = await kv.get('sync', 'settings');
return { ...DEFAULT_SETTINGS, ...stored };
};
// Update settings
export const updateSettings = async (updates: Partial<Settings>): Promise<void> => {
const current = await getSettings();
const newSettings = { ...current, ...updates };
await kv.set('sync', 'settings', newSettings);
};
// Validate theme
export const isValidTheme = (theme: string): theme is Settings['theme'] => {
return ['light', 'dark', 'auto'].includes(theme);
};
Usage:
import { getSettings, updateSettings } from '@/shared/lib/setting';
// Get current settings
const settings = await getSettings();
console.log(settings.theme); // 'auto'
// Update theme
await updateSettings({ theme: 'dark' });
Utility Functions
Location: src/shared/lib/utils.ts
General-purpose utility functions.
Common Patterns
// Debounce function
export const debounce = <T extends (...args: any[]) => any>(
fn: T,
delay: number
): ((...args: Parameters<T>) => void) => {
let timer: ReturnType<typeof setTimeout>;
return (...args: Parameters<T>) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
};
// Throttle function
export const throttle = <T extends (...args: any[]) => any>(
fn: T,
limit: number
): ((...args: Parameters<T>) => void) => {
let inThrottle: boolean;
return (...args: Parameters<T>) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
};
// Sleep helper
export const sleep = (ms: number): Promise<void> => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
// Retry helper
export const retry = async <T>(
fn: () => Promise<T>,
retries = 3,
delay = 1000
): Promise<T> => {
try {
return await fn();
} catch (err) {
if (retries === 0) throw err;
await sleep(delay);
return retry(fn, retries - 1, delay);
}
};
Usage:
import { debounce, retry, sleep } from '@/shared/lib/utils';
// Debounced search
const debouncedSearch = debounce(async (query: string) => {
const results = await fetch(`/api/search?q=${query}`);
displayResults(results);
}, 300);
// Retry failed requests
const fetchWithRetry = async () => {
return retry(
async () => {
const res = await fetch('/api/data');
if (!res.ok) throw new Error('Request failed');
return res.json();
},
3, // 3 retries
1000 // 1 second delay
);
};
Constants
Location: src/shared/constants.ts
Global constants and configuration.
FLAGS
Feature flags for conditional behavior:
export const FLAGS = {
ENABLE_OVERLAY: true,
ENABLE_ANALYTICS: false,
DEBUG_MODE: process.env.NODE_ENV === 'development'
} as const;
Usage:
import { FLAGS } from '@/shared/constants';
if (FLAGS.ENABLE_OVERLAY) {
mountOverlay();
}
ALARMS
Alarm identifiers for scheduled tasks:
export const ALARMS = {
POLL: 'poll',
DAILY_CLEANUP: 'daily_cleanup',
SYNC_DATA: 'sync_data'
} as const;
Usage:
import { ALARMS } from '@/shared/constants';
chrome.alarms.create(ALARMS.POLL, { periodInMinutes: 5 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === ALARMS.POLL) {
pollData();
}
});
RESTRICTED
URL patterns to avoid:
export const RESTRICTED = {
schemes: ['chrome', 'chrome-extension', 'chrome-untrusted', 'devtools', 'edge', 'about'],
hosts: [
/^(?:https?:\/\/)?chrome\.google\.com\/webstore\/?/i,
/^(?:https?:\/\/)?microsoftedge\.microsoft\.com\/addons\/?/i
]
} as const;
Usage:
import { RESTRICTED } from '@/shared/constants';
const isRestricted = (url: string): boolean => {
const urlObj = new URL(url);
// Check scheme
if (RESTRICTED.schemes.includes(urlObj.protocol.replace(':', ''))) {
return true;
}
// Check host patterns
return RESTRICTED.hosts.some((pattern) => pattern.test(url));
};
// In content script injection
chrome.tabs.query({}, (tabs) => {
tabs.forEach((tab) => {
if (tab.url && !isRestricted(tab.url)) {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['content.js']
});
}
});
});
Best Practices
1. Use Type Definitions
Always import types for better IDE support:
import type { StorageSchema } from '@/shared/types';
import type { MessageMap } from '@/shared/types';
2. Centralize Constants
Keep all magic values in constants.ts:
// ✓ Good
import { ALARMS } from '@/shared/constants';
chrome.alarms.create(ALARMS.POLL, { periodInMinutes: 5 });
// ❌ Bad
chrome.alarms.create('poll', { periodInMinutes: 5 });
3. Create Utility Wrappers
Wrap common patterns in reusable utilities:
// utils.ts
export const queryActiveTab = async (): Promise<chrome.tabs.Tab | undefined> => {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
return tabs[0];
};
// Usage
const activeTab = await queryActiveTab();
4. Document Utility Functions
/**
* Debounces a function call
* @param fn - Function to debounce
* @param delay - Delay in milliseconds
* @returns Debounced function
*/
export const debounce = <T extends (...args: any[]) => any>(
fn: T,
delay: number
) => {
// Implementation...
};
Next Steps
- Learn about Development Workflow
- Explore Testing utilities
- Check API Reference for type definitions