Spec
Complete API reference for @smart-mailto/core. Types, functions, configuration, and architecture.
Parser
RFC 6068-compliant mailto: URI parsing and building. All string values are decoded (not URL-encoded) in the output.
parseMailto(href)
Parses a mailto: URI string into a structured MailtoParams object.
const params = parseMailto(
'mailto:hello@example.com?cc=boss@site.com&subject=Hi&body=Hello'
);
// → {
// to: ['hello@example.com'],
// cc: ['boss@site.com'],
// subject: 'Hi',
// body: 'Hello'
// }buildMailtoHref(params)
Serializes a MailtoParams object back into a mailto: URI string.
buildMailtoHref({
to: ['hello@example.com'],
subject: 'Hello',
body: 'World'
});
// → 'mailto:hello@example.com?subject=Hello&body=World'isValidMailtoParams(params)
Returns true if the params object has at least one recipient.
Resolver
Resolves which providers to show for a given mailto: context. Resolution order: custom → persisted preference → email domain detection → preferredProvider override → geo-ordered list.
const params = parseMailto('mailto:hello@gmail.com');
const resolved = resolveProviders(params, { autoDetectGeo: true });
// resolved: ResolvedProviders
// {
// providers: Provider[], // ordered list to display
// detectedRegion: string, // e.g. 'ru' | 'global'
// signals: GeoSignals, // raw browser signals
// detectedFromEmail: string | null // provider ID if domain matched
// }Live: resolveProviders
Paste any email to see geo detection and provider resolution
Region: Global
Signals: UTC · en-US
Providers: gmail, outlook-personal, yahoo, icloud
Domain match: gmail
Geo Detection
Zero-network geo detection using only browser APIs. Maps IANA timezone + navigator.language to regional provider priorities. Runs in <1ms, no external requests.
collectGeoSignals()
Timezone: UTC
Locale: en-US
Languages: en-US
Mobile: no
iOS: no
Android: no
detectRegionLabel(signals)
Returns a human-readable region string:
Europe/Moscow → Russia/CIS
Asia/Tokyo → Japan
America/New_York → Global
Email Domain Detector
Maps email domains to provider IDs. Used during resolution to highlight the user's existing email provider.
detectProviderFromEmail('hello@gmail.com');
// → 'gmail'
detectProviderFromEmail('user@outlook.com');
// → 'outlook-personal'
detectProviderFromEmail('user@protonmail.com');
// → 'protonmail'
getDomainsForProvider('gmail');
// → ['gmail.com', 'googlemail.com']Storage
localStorage-based preference persistence. All operations are SSR-safe and silently fail in incognito/private browsing.
savePreference('gmail'); // Persist user's choice
loadPreference(); // → 'gmail' | null
clearPreference(); // Remove persisted choice
isStorageAvailable(); // → boolean (false in SSR/incognito)Initialization
Global initialization via capture-phase event delegation on document. Intercepts all mailto: link clicks. Modal and icons are dynamically imported to keep the core bundle tiny.
import { initSmartMailto, destroySmartMailto, isInitialized, updateConfig } from '@smart-mailto/core';
const destroy = initSmartMailto({
theme: 'dark',
autoDetectGeo: true,
maxProviders: 6,
// ...config
});
destroy(); // Remove listeners, clean up
isInitialized(); // → boolean
updateConfig({ theme: 'light' }); // Update config at runtimeArchitecture notes
- Uses capture-phase (
capture: true) ondocumentto intercept all mailto: links including dynamically added ones - Modal (
modal.ts) and icons (icons.ts) are dynamically imported on first trigger - Safari popup blocker:
window.open()is called synchronously within the user click handler - Shadow DOM is used for modal CSS isolation — host page styles cannot leak in
SmartMailtoConfig
Full configuration interface. All options are optional and have sensible defaults.
| Name | Type | Description |
|---|---|---|
theme(opt) | 'dark' | 'light' | 'auto' | Visual theme for the modal. Defaults to auto (prefers-color-scheme).Default: 'auto' |
autoDetectGeo(opt) | boolean | Use browser heuristics to order providers by region. Defaults to true.Default: true |
preferredProvider(opt) | string | Force a specific provider ID to the top of the list. |
maxProviders(opt) | number | Maximum number of provider buttons to show.Default: 6 |
includeNative(opt) | boolean | Include "Open in Native Mail App" option.Default: true on mobile |
includeCopy(opt) | boolean | Always include a "Copy Email Address" button.Default: true |
excludeProviders(opt) | string[] | Hide specific provider IDs from the list. |
customProviders(opt) | Provider[] | Inject custom/enterprise providers. Prepended to the list. |
classNames(opt) | ClassNames | CSS class overrides for headless/unstyled mode. |
i18n(opt) | Partial<I18nStrings> | Override UI strings for internationalization. |
rememberChoice(opt) | boolean | Persist user's provider choice to localStorage.Default: true |
storageKey(opt) | string | localStorage key for persisted provider choice.Default: 'smart-mailto:preferred' |
onOpen(opt) | (provider, params) => void | Lifecycle hook: fired when user clicks a provider. |
onCopy(opt) | (email) => void | Lifecycle hook: fired when user copies an email address. |
onClose(opt) | () => void | Lifecycle hook: fired when modal is dismissed. |
onShow(opt) | (params, providers) => void | Lifecycle hook: fired when modal is first shown. |
Provider Registry
32+ email providers with verified compose URLs. Registry is in packages/core/src/providers.ts.
PROVIDERS // ReadonlyRecord<string, Provider> getProvider(id) // → Provider | undefined getAllProviders() // → Provider[]
Live: Provider Lookup
Type a provider ID to inspect its config
Name: Gmail
Color: #EA4335
Regions: global
noBodyPreFill: false
Framework Wrappers
React
// SmartMailtoProvider wraps your app
<SmartMailtoProvider theme="dark" autoDetectGeo>
<App />
</SmartMailtoProvider>
// useSmartMailto() for programmatic control
const { open, config } = useSmartMailto();
open('hello@example.com', { subject: 'Hi' });
// SmartMailto drop-in component replaces <a>
<SmartMailto href="mailto:hello@example.com" theme="dark">
Contact Us
</SmartMailto>Live: React Component
SmartMailto component with dark/light sync
Vue 3
// main.ts — register plugin
import { SmartMailtoPlugin } from '@smart-mailto/vue';
app.use(SmartMailtoPlugin, { theme: 'dark', autoDetectGeo: true });
// SmartMailto component (auto-registered)
<SmartMailto href="mailto:hello@example.com" theme="dark">
Contact Us
</SmartMailto>Svelte
// smartMailto action on any anchor
<a href="mailto:hello@example.com"
use:smartMailto={{ theme: 'dark' }}>
Contact Us
</a>
// Global init for full-page interception
import { initGlobal, destroyGlobal } from '@smart-mailto/svelte';
onMount(() => { initGlobal({ theme: 'dark' }); });
onDestroy(() => destroyGlobal());Bundle & Performance
Bundle Size
< 8KB
gzipped, all targets
Enforced in CI via .github/workflows/bundle-size.yml
Modal Loading
Core bundle contains only parsing, resolution, and geo logic.
Modal UI + icons are dynamically imported on first trigger, keeping initial load minimal.
Build targets