npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

adapter-electron

v1.0.7

Published

A SvelteKit adapter for Electron Desktop Apps using protocol interception

Readme

adapter-electron

A SvelteKit adapter for Electron desktop apps that uses native protocol handling for production and seamless Vite dev server integration for development.

Features

  • Native Protocol Handling: Uses Electron's protocol.handle() API for production
  • Development Integration: Seamless Vite dev server integration with HMR
  • No HTTP Server: Bypasses Node.js HTTP servers entirely in production
  • Full SvelteKit Support: SSR, API routes, static assets, prerendered pages, form actions
  • Clean Architecture: All Electron integration code is encapsulated
  • Production Ready: Works with electron-builder and similar tools
  • TypeScript Support: Full type definitions included
  • Proper Error Handling: User-friendly error reporting with GitHub issue links

Installation

npm install adapter-electron

Quick Start

1. Configure SvelteKit

In your svelte.config.js:

import adapter from 'adapter-electron';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	preprocess: vitePreprocess(),

	kit: {
		adapter: adapter()
	}
};

export default config;

2. Set up Vite Configuration

In your vite.config.ts:

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { electronPlugin } from 'adapter-electron';

export default defineConfig({
	plugins: [
		sveltekit(),
		electronPlugin({
			// Auto-detects src/main.ts and src/preload.ts by default
			// Override paths if needed:
			// mainEntry: 'src/main.ts',        // Main process entry (default: 'src/main.ts')
			// preloadEntry: 'src/preload.ts',  // Preload script entry (default: 'src/preload.ts')
			// mainOut: 'out/main/index.js',    // Main output (default: 'out/main/index.js')
			// preloadOut: 'out/preload/index.js' // Preload output (default: 'out/preload/index.js')
		})
	]
});

3. Create Electron Main Process

Create src/main.ts:

import { app, BrowserWindow } from 'electron';
import { setupHandler, getPreloadPath, registerAppScheme } from 'adapter-electron/functions/setupHandler';

// Electron log is the best way I've found to get logs from compiled binaries
import log from 'electron-log/main';

console.log = log.log;

let mainWindow: BrowserWindow | null = null;
let stopIntercept: (() => void) | undefined;

process.on('SIGTERM', () => process.exit(0));
process.on('SIGINT', () => process.exit(0));

// First register the app scheme
registerAppScheme();

async function createWindow() {
	// Create the browser window
	mainWindow = new BrowserWindow({
		width: 1200,
		height: 800,
		webPreferences: {
			// Second configure the preload script
			preload: getPreloadPath(),
			contextIsolation: true,
			devTools: true
		}
	});

	mainWindow.once('ready-to-show', () => mainWindow?.webContents.openDevTools());

	mainWindow.on('closed', () => {
		mainWindow = null;
		stopIntercept?.();
	});

	// Third, Setup the handler
	stopIntercept = await setupHandler(mainWindow);

	return mainWindow;
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
	if (process.platform !== 'darwin') {
		app.quit();
	}
});

app.on('activate', async () => {
	if (BrowserWindow.getAllWindows().length === 0 && !mainWindow) {
		try {
			await createWindow();
		} catch (error) {
			console.error('Failed to create window:', error);
		}
	}
});

4. Create Preload Script

Create src/preload.ts:

console.log('Preload loaded');

5. Configure Package.json

Add these fields to your package.json:

{
  "main": "./out/main/index.cjs",
  "scripts": {
    "sync": "svelte-kit sync",
	"dev": "pnpm sync && concurrently \"vite dev\" \"wait-on http://localhost:5173 && nodemon --watch out --exec electron .\" --names \"sveltekit,electron\" --prefix-colors \"#ff3e00,blue\"",
    "build": "pnpm sync && vite build",
    "build:all": "pnpm build && electron-builder -mwl --config",
    "build:win": "pnpm build && electron-builder --win --config",
    "build:mac": "pnpm build && electron-builder --mac --config",
    "build:linux": "pnpm build && electron-builder --linux --config"
  }
}

6. Build and Run

# Development (uses Vite dev server with HMR)
npm run dev

# Production build
npm run build

# Package for distribution
npm run build:all  # All platforms
npm run build:win  # Windows only
npm run build:mac  # macOS only
npm run build:linux # Linux only

How It Works

Development Mode

  • Uses Vite dev server (http://localhost:5173 by default)
  • Full hot module replacement (HMR) support
  • No protocol interception needed
  • Set VITE_DEV_SERVER environment variable to customize dev server URL

Production Mode

The adapter uses Electron's native protocol.handle() API to intercept http://127.0.0.1 requests:

  1. Static Assets: Serves files from the client/ directory
  2. Prerendered Pages: Serves static HTML from the prerendered/ directory
  3. SSR/API Routes: Calls SvelteKit's Server.respond() directly
  4. Form Actions: Full support for SvelteKit form actions
  5. Cookie Handling: Automatic cookie synchronization with Electron session

Build Output Structure

After running npm run build, you'll have:

out/
├── client/                  # SvelteKit client assets (JS, CSS, images)
├── server/                  # SvelteKit server files for SSR
│   ├── index.js            # SvelteKit server
│   ├── manifest.js         # App manifest
│   └── chunks/             # Server chunks
├── prerendered/            # Prerendered static HTML pages
├── functions/              # Protocol handler code
│   ├── setupHandler.js     # Main protocol handler
│   └── setupHandler.d.ts   # TypeScript definitions
├── main/                   # Compiled main process
│   └── index.js
└── preload/                # Compiled preload script
    └── index.js

Configuration Options

SvelteKit Adapter Options

adapter({
	out: 'out',              // Output directory (default: 'out')
	precompress: false       // Precompress assets with gzip/brotli (default: false)
})

| Option | Type | Default | Description | | ------------- | --------- | ------- | ----------------------------------------------- | | out | string | 'out' | The directory to write the built application to | | precompress | boolean | false | Whether to precompress assets with gzip/brotli |

Electron Plugin Options

electronPlugin({
	mainEntry: 'src/main.ts',           // Main process entry point (default: 'src/main.ts')
	preloadEntry: 'src/preload.ts',     // Preload script entry point (default: 'src/preload.ts')
	mainOut: 'out/main/index.cjs',      // Main process output (default: 'out/main/index.cjs')
	preloadOut: 'out/preload/index.js', // Preload script output (default: 'out/preload/index.js')
	externalMain: ['electron', 'electron-log', 'electron-is-dev', 'SERVER', 'MANIFEST'], // External dependencies for main process
	externalPreload: ['electron']       // External dependencies for preload script
})

| Option | Type | Default | Description | | ----------------- | ---------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------ | | mainEntry | string | 'src/main.ts' | Path to the main process entry file | | preloadEntry | string | 'src/preload.ts' | Path to the preload script entry file | | mainOut | string | 'out/main/index.cjs' | Output path for the compiled main process | | preloadOut | string | 'out/preload/index.js' | Output path for the compiled preload script | | externalMain | string[] | ['electron', 'electron-log', 'electron-is-dev', 'SERVER', 'MANIFEST'] | External dependencies that won't be bundled for the main process | | externalPreload | string[] | ['electron'] | External dependencies that won't be bundled for the preload script |

Configuration Examples

Custom Output Directory

// svelte.config.js
import adapter from 'adapter-electron';

export default {
	kit: {
		adapter: adapter({
			out: 'dist/electron'
		})
	}
};

Custom Entry Points

// vite.config.ts
import { electronPlugin } from 'adapter-electron';

export default defineConfig({
	plugins: [
		sveltekit(),
		electronPlugin({
			mainEntry: 'electron/main.ts',
			preloadEntry: 'electron/preload.ts',
			mainOut: 'dist/electron/main.js',
			preloadOut: 'dist/electron/preload.js'
		})
	]
});

Custom External Dependencies

// vite.config.ts
import { electronPlugin } from 'adapter-electron';

export default defineConfig({
	plugins: [
		sveltekit(),
		electronPlugin({
			externalMain: ['electron', 'electron-log', 'my-custom-package'],
			externalPreload: ['electron', 'another-package']
		})
	]
});

Environment Variables

| Variable | Description | Default | | ----------------- | ---------------------- | ----------------------- | | VITE_DEV_SERVER | Development server URL | http://localhost:5173 | | VITE_APP_URL | Production app URL | http://127.0.0.1 |

API Reference

Protocol Handler Functions

setupHandler(mainWindow: BrowserWindow): Promise<() => void>

Sets up the protocol handler and loads the appropriate URL based on environment.

  • Development: Loads Vite dev server
  • Production: Sets up protocol interception and loads app
  • Returns: Cleanup function to stop protocol interception

registerAppScheme(): void

Registers the HTTP scheme as privileged. Must be called before app.ready.

getPreloadPath(): string

Returns the correct preload script path for current environment.

  • Development: Points to source preload script
  • Production: Points to built preload script

Request Flow

Development:
Electron Window → Vite Dev Server (http://localhost:5173)
                     ↓
                 Hot Module Replacement

Production:
Electron Request (http://127.0.0.1/page)
    ↓
Protocol Handler
    ↓
1. Check static files (client/)
2. Check prerendered pages (prerendered/)  
3. Handle SSR/API (server.respond())
    ↓
Response with Cookie Sync

Advanced Usage

Custom Error Handling

The adapter includes built-in error reporting. Errors are:

  • Logged to console in development
  • Shown as dialog boxes in production
  • Include GitHub issue reporting instructions

Cookie Management

Cookies are automatically synchronized between SvelteKit and Electron's session:

  • Request cookies are extracted from Electron session
  • Response Set-Cookie headers are applied to Electron session
  • Supports all cookie attributes (secure, httpOnly, etc.)

Security Features

  • Path traversal protection for static files
  • Automatic CORS handling
  • Secure cookie handling
  • Context isolation enforced

Production Packaging

With electron-builder

Create electron-builder.yaml:

appId: com.electron.app
productName: electron-sveltekit
directories:
  buildResources: build
files:
  - "!**/.vscode/*"
  - "!src/*"
  - "!electron.vite.config.{js,ts,mjs,cjs}"
  - "!vite.electron.config.ts"
  - "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}"
  - "!{.env,.env.*,.npmrc,pnpm-lock.yaml}"
  - "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}"
  - "out/**/*"
asarUnpack:
  - resources/**
win:
  target: ["portable"]
  executableName: electron-sveltekit
nsis:
  artifactName: ${name}-${version}-setup.${ext}
  shortcutName: ${productName}
  uninstallDisplayName: ${productName}
  createDesktopShortcut: always
mac:
  entitlementsInherit: build/entitlements.mac.plist
  extendInfo:
    - NSCameraUsageDescription: Application requests access to the device's camera.
    - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
    - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
    - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
dmg:
  artifactName: ${name}-${version}.${ext}
linux:
  target:
    - AppImage
    - snap
    - deb
  maintainer: electronjs.org
  category: Utility
appImage:
  artifactName: ${name}-${version}.${ext}
npmRebuild: false
publish:
  provider: generic
  url: https://example.com/auto-updates

With electron-forge

// forge.config.js
module.exports = {
  packagerConfig: {
    dir: './out'
  },
  makers: [
    {
      name: '@electron-forge/maker-squirrel',
      config: {}
    }
  ]
};

Dependencies

The adapter requires these additional dependencies:

npm install electron electron-builder electron-log concurrently
npm install -D @types/node

Troubleshooting

Common Issues

Protocol not working in production:

  • Ensure registerAppScheme() is called before app.ready
  • Check that setupHandler() is called after window creation

Development server not loading:

  • Verify Vite dev server is running on expected port
  • Set VITE_DEV_SERVER environment variable if using custom port

Form actions not working:

  • Ensure you're using proper Web API Request objects (handled automatically)
  • Check that cookies are being synchronized properly

Build errors:

  • Verify all dependencies are installed
  • Check that TypeScript configuration includes Electron types
  • Ensure concurrently is installed for development scripts

Debug Mode

Enable verbose logging:

// In main process
process.env.ELECTRON_ENABLE_LOGGING = 'true';

Getting Help

If you encounter issues:

  1. Check the console for error messages
  2. Verify your configuration matches the examples
  3. File an issue with error details and reproduction steps

License

MIT