Electron integration for Remix ⚛💿

Overview

remix-electron

Electron integration for Remix

demo screenshot

Setup

Use degit to create a new project from the template.

npx degit itsMapleLeaf/remix-electron/template my-desktop-app

Adding to an existing Remix project

Install remix-electron and peer dependencies:

npm i remix-electron electron @remix-run/node @remix-run/server-runtime react react-dom

Add a file at desktop/main.js to run the electron app. The initRemix function returns a url to load in the browser window.

{ try { const url = await initRemix({ serverBuild: join(__dirname, "build"), }) win = new BrowserWindow({ show: false }) await win.loadURL(url) win.show() } catch (error) { console.error(error) } })">
// desktop/main.js
const { initRemix } = require("remix-electron")
const { app, BrowserWindow } = require("electron")
const { join } = require("node:path")

let win

app.on("ready", async () => {
  try {
    const url = await initRemix({
      serverBuild: join(__dirname, "build"),
    })

    win = new BrowserWindow({ show: false })
    await win.loadURL(url)
    win.show()
  } catch (error) {
    console.error(error)
  }
})

Update serverBuildPath in your Remix config:

// remix.config.js
/**
 * @type {import('@remix-run/dev/config').AppConfig}
 */
module.exports = {
  serverBuildPath: "desktop/build/index.js",
  // ...
}

Build the app with npm run build, then run npx electron desktop/main.js to start the app! 🚀

Using Electron APIs

Importing "electron" directly in route files results in Electron trying to get bundled and called in the browser / renderer process.

To circumvent this, create a electron.server.ts file, which re-exports from electron. The .server suffix tells Remix to only load it in the main process. You should use .server for any code that runs in the main process and uses node/electron APIs.

// app/electron.server.ts
// @ts-nocheck
import electron from "electron"
export = electron

Likewise, for any code running in the renderer process, e.g. using the clipboard module, you can use the .client suffix. Renderer process modules require nodeIntegration.

// desktop/main.ts
function createWindow() {
  // ...
  win = new BrowserWindow({
    // ...
    webPreferences: {
      nodeIntegration: true,
    },
  })
}

API

async initRemix({ serverBuild[, publicFolder, mode, getLoadContext] })

Initializes remix-electron. Returns a promise with a url to load in the browser window.

Options:

  • serverBuild: The path to your server build (e.g. path.join(__dirname, 'build')), or the server build itself (e.g. required from @remix-run/dev/server-build). Updates on refresh are only supported when passing a path string.

  • mode: The mode the app is running in. Can be "development" or "production". Defaults to "production" when packaged, otherwise uses process.env.NODE_ENV.

  • publicFolder: The folder where static assets are served from, including your browser build. Defaults to "public". Non-relative paths are resolved relative to app.getAppPath().

  • getLoadContext: Use this to inject some value into all of your remix loaders, e.g. an API client. The loaders receive it as context

Load context TS example

app/context.ts

& { context: LoadContext }">
import type * as remix from "@remix-run/server-runtime"

// your context type
export type LoadContext = {
  secret: string
}

// a custom data function args type to use for loaders/actions
export type DataFunctionArgs = Omit<remix.DataFunctionArgs, "context"> & {
  context: LoadContext
}

desktop/main.js

({ secret: "123", }), })">
const url = await initRemix({
  // ...

  /** @type {import("~/context").LoadContext} */
  getLoadContext: () => ({
    secret: "123",
  }),
})

In a route file:

import type { DataFunctionArgs, LoadContext } from "~/context"

export async function loader({ context }: DataFunctionArgs) {
  // do something with context
}

Motivation

Electron has a comprehensive list of security recommendations to follow when building an app, especially if that app interacts with the web. Which includes, but is not limited to:

  • Using preload.js files to expose specific electron functionality to your app, via globals
  • Using IPC communication
  • Avoiding remote.require (which has since been removed)

These practices can lead to a lot of awkward boilerplate and splitting up related code across multiple files and domains.

With remix-electron, you can freely use Electron APIs in Remix loader functions. It's a Node process with full Node capabilities, with access to the full Electron API, none of which runs in the browser.

The browser only receives data and renders a view. Additionally, you can neatly colocate your main process code right beside the related renderer code in a route file.

Thinking about it another way: it's like a normal Remix web app, except Electron is your backend.

Comments
  • 💡 Feature Request: add .`vscode` folder with debug options in `remix-electron` template

    💡 Feature Request: add .`vscode` folder with debug options in `remix-electron` template

    Scenario

    As a user who has just downloaded the remix-electron template, I would like to be able to debug my remix electron app using VS Code's built in debugger.

    Motivation for the Feature/Change?

    Having a .vscode folder with the settings.json file already preconfigured for a user would allow user:

    • do step through debugging in their VS Code editor.
    • user will not need to figure out how to do this later (enhanced developer experience DX)

    Demo

    See the YouTube video below created by @kiliman (another awesome remix advocate 💿 )

    • https://gist.github.com/kiliman/a9d7c874af03369a1d105a92560d89e9

    There likely needs to be some tweaking to ensure it opens debugs the right port / process. I took a peek at another electron app's .vscode folder and they had this (likely need to tweak):

    Electron docs: https://www.electronjs.org/docs/latest/tutorial/debugging-vscode

    // Sample file (not tested)
    {
      "version": "0.2.0",
      "configurations": [
      {
        "version": "0.2.0",
        "configurations": [
          {
            "name": "Debug Main Process",
            "type": "node",
            "request": "launch",
            "cwd": "${workspaceFolder}",
            "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
            "windows": {
              "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
            },
            "args" : ["."],
            "outputCapture": "std"
          }
        ]
      },
      // TODO: change to vitest
      {
          "type": "node",
          "request": "launch",
          "name": "Jest Tests",
          "program": "${workspaceRoot}/node_modules/.bin/jest",
          "args": ["--config", "jest.json", "--runInBand"],
          "console": "internalConsole",
          "internalConsoleOptions": "neverOpen"
      },
      {
          "type": "chrome",
          "request": "attach",
          "name": "Electron Renderer",
          "port": 9223,
          "webRoot": "${workspaceFolder}",
          "timeout": 10000
      }
      ]
    }
    

    Platform

    • Operating System: All
    enhancement 
    opened by cliffordfajardo 3
  • Using express is a (potential and unnecessary) security issue

    Using express is a (potential and unnecessary) security issue

    Using express opens up a port on the end users machine that any application can access, even a regular web browser. If there's any private info being served up a random website could potentially steal it or if the version of electron/node you're shipping with has another security issue in the http.Server implementation all of your users could get pwned.

    To do this in a safe way we should be using Electron Custom Protocols via the protocol module.

    Ref: https://electronjs.org/docs/latest/api/protocol

    Not sure on the specifics of remix but you should be able to wire it up into a custom protocol that is substantially safer to use than a localhost server.

    opened by MarshallOfSound 3
  • Wrong asset files paths on Windows in 0.3.0

    Wrong asset files paths on Windows in 0.3.0

    Describe the bug

      // asset-files.ts / collectAssetFiles()
      return files.map((file) => ({
        path: "/" + relative(folder, file),
        content: () => readFile(file),
      }))
    

    This produces wrong file paths on Windows because fast-glob will return paths such as

    C:/my-desktop-app/public/build/entry.client-WZGREWZ5.js
    

    and path.relative mangles it into

    /build\\entry.client-WZGREWZ5.js
    

    Quick solution for me was to replace the slashes relative(folder, path).replace(/\\/g, '/') but normalize-path or unixify mentioned in fast-glob readme might be a more robust solution.

    Minimal Reproduction

    ~

    Steps to Reproduce the Bug or Issue

    ~

    Expected behavior

    ~

    Screenshots or Videos

    No response

    Platform

    • OS: Windows 10

    Additional context

    No response

    opened by Aexylus 2
  • 💡 Feature Request: add React devtools

    💡 Feature Request: add React devtools

    Scenario

    As a remix-electron user, it would be nice to be able to see inspect the browser devtools and see the react devtools tab there. This would allow users inspect to state of their React application once its rendered in the browser window

    Motivation for change

    • save devs time from setting this ip
    • help developers by enabling them to debug/inspect their React app's state etc..

    CleanShot 2022-03-04 at 02 50 12@2x

    References

    • https://www.electronjs.org/docs/latest/tutorial/devtools-extension
    opened by cliffordfajardo 1
  • Fix del not working on Windows

    Fix del not working on Windows

    As per del-cli readme: Windows users: Since $ del is already a builtin command on Windows, you need to use $ del-cli there.

    Don't have an easy way to test this on mac/linux, but del-cli should hopefully work everywhere.

    opened by Aexylus 1
  • add github ISSUE_TEMPLATE files

    add github ISSUE_TEMPLATE files

    What is the change?

    Add bug_report.yml & config.yml file to enable Github's form based issue template

    • https://youtu.be/qQE1BUkf2-s?t=23

    Motivation

    • encourage's bug reporter's to put more care into their bug report before submission
    • this may help maintainer's receive more detailed & higher quality bug report's
    • adds helpful tips for user's during the process of creating a bug/issue report

    Demo of Change

    this PR is similar to this one I created here for another repo recently

    • https://github.com/antvis/G6/blob/master/.github/ISSUE_TEMPLATE/bug_report.yml
    opened by cliffordfajardo 1
  • Improve request performance in dev

    Improve request performance in dev

    When the server build gets larger it takes longer for require(serverBuildOption) to load it and process it. In my case where the build file was only around 1.8MB it took 150ms even though I have it on SSD. And since it's being re-required on every single request it can take even longer. In one of my routes I had fetcher that caused 3 requests thus taking around half a second for my click to get a result.

    My quick solution is to check the build file changed time on every request and require it only when it changes. Ideally you'd require the build only when the remix server build version changes but afaik you can't access the server build info outside of remix.

    opened by Aexylus 0
  • Support reload on change workflow again

    Support reload on change workflow again

    This change updates the options for initRemix to also accept a path to the server build. A path will make it behave as it did before v1.0.0: on each HTTP request, purge the require cache, then require the server build. This should allow changes to be shown on reload again, without having to restart the entire electron app.

    Passing the full server build is still supported, so this isn't a breaking change, but seeing changes on refresh requires passing a path.

    Using a separate server build also means we can't use the server build feature for a TS entry anymore :smiling_face_with_tear: but if it's between that and reload on change, I'll pick the second any day.

    enhancement 
    opened by itsMapleLeaf 0
  • Accept server build instead of remix config

    Accept server build instead of remix config

    opened by itsMapleLeaf 0
  • 💡 Feature Request Idea:  help beginner electron users get started on good path with this Main script

    💡 Feature Request Idea: help beginner electron users get started on good path with this Main script

    Describe the bug

    Scenario

    As a user who is new to electron, I want to make sure I am getting started on the right path to success when coding my electron app. The desktop/main.js the entry point of the electron app in my experience can get messy and can easily become a place where lots of logic is placed there, its easy to create lots of top level variable there and create a mess, if one is not careful.

    Proposed Solution

    Create a file called electron-main which encapsulates the bootstrapping of the electron application into a Javascript class. The advantage of this is that we could now test the bootstrapping portions of our application like.

    // FILENAME: electron-main.ts
    // NOTE: this the code needs some tweaking, this is a high level idea
    
    import { BrowserWindow } from 'electron';
    export default class Main {
        static mainWindow: Electron.BrowserWindow;
        static application: Electron.App;
        static BrowserWindow;
        private static onWindowAllClosed() {
            if (process.platform !== 'darwin') {
                Main.application.quit();
            }
        }
    
        private static onClose() {
            // Dereference the window object. 
            Main.mainWindow = null;
        }
    
        private static onReady() {
            Main.mainWindow = new Main.BrowserWindow({ width: 800, height: 600 });
            Main.mainWindow
                .loadURL('file://' + __dirname + '/index.html');
            Main.mainWindow.on('closed', Main.onClose);
        }
        
         // NOTE: we could use a constructor() instead 
         // Also code can be updated to follow the single pattern beter
        static main(app: Electron.App, browserWindow: typeof BrowserWindow) {
            // we pass the Electron.App object and the  
            // Electron.BrowserWindow into this function 
            // so this class has no dependencies. This 
            // makes the code easier to write tests for 
            Main.BrowserWindow = browserWindow;
            Main.application = app;
            Main.application.on('window-all-closed', Main.onWindowAllClosed);
            Main.application.on('ready', Main.onReady);
        }
    }
    
    // FILENAME: electron-app.ts
    import { app, BrowserWindow } from 'electron';
    import Main from './Main';
    
    Main.main(app, BrowserWindow);
    

    Credits

    • https://davembush.medium.com/typescript-and-electron-the-right-way-141c2e15e4e1
    enhancement wontfix 
    opened by cliffordfajardo 1
  • Feat: Converted the main EP to typescript.

    Feat: Converted the main EP to typescript.

    Extended the base tsconfig to cover the new main.ts entry point.

    Added a new build:desktop script to build the main.ts entry point to JS.

    Added the aforementioned script to the dev and build scripts so the file is rebuilt before the app is launched.

    enhancement 
    opened by ITninja04 2
Releases(1.2.2)
  • 1.2.2(Apr 15, 2022)

    Bugfix: resolves non-absolute paths relative to the app folder. Should fix cases where assets like css files aren't resolved

    Also updated the template to use the latest Remix version

    Source code(tar.gz)
    Source code(zip)
  • 1.2.1(Feb 24, 2022)

  • 1.2.0(Feb 24, 2022)

  • 1.1.0(Feb 20, 2022)

  • 1.0.0(Feb 18, 2022)

    Breaking: The initRemix() function now accepts the server build instead of the remix config. This update allows using Remix's server build feature with the virtual import; you can now more easily write the electron entry in TS!

    // desktop/main.ts
    import * as serverBuild from "@remix-run/dev/server-build"
    import { initRemix } from "remix-electron"
    
    // ...
    
        const url = await initRemix({ serverBuild })
        await createWindow(url)
    
    // remix.config.js
    /**
     * @type {import('@remix-run/dev/config').AppConfig}
     */
    module.exports = {
      appDirectory: "app",
      assetsBuildDirectory: "public/build",
      publicPath: "/build/",
      server: "desktop/main.ts",
      serverBuildPath: "desktop/build/index.js",
      devServerPort: 8002,
      ignoredRouteFiles: [".*"],
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 0.3.1(Feb 17, 2022)

  • 0.3.0(Feb 17, 2022)

  • 0.2.0(Feb 12, 2022)

Owner
Darius
JS dev, rhythm gamer, EDM nerd, lover of cute things
Darius
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Secure-electron-template - The best way to build Electron apps with security in mind.

secure-electron-template A current electron app template with the most popular frameworks, designed and built with security in mind. (If you are curio

reZach 1.4k Dec 29, 2022
Connect Web Integration illustrates the integration of Connect-Web in various JS frameworks and tooling

Connect Web Integration Connect Web Integration is a repository of example projects using Connect-Web with various JS frameworks and tooling. It provi

Buf 43 Dec 29, 2022
The Remix Stack for deploying to Vercel with testing, linting, formatting, structure and mock for 3rd party API integration.

Remix DnB Stack See it live: https://dnb-stack.vercel.app/ Learn more about Remix Stacks. npx create-remix --template robipop22/dnb-stack What's in th

Robert Pop 61 Dec 13, 2022
The Remix version of the fakebooks app demonstrated on https://remix.run. Check out the CRA version: https://github.com/kentcdodds/fakebooks-cra

Remix Fakebooks App This is a (very) simple implementation of the fakebooks mock app demonstrated on remix.run. There is no database, but there is an

Kent C. Dodds 61 Dec 22, 2022
Remix Stack for deploying to Vercel with remix-auth, Planetscale, Radix UI, TailwindCSS, formatting, linting etc. Written in Typescript.

Remix Synthwave Stack Learn more about Remix Stacks. npx create-remix --template ilangorajagopal/synthwave-stack What's in the stack Vercel deploymen

Ilango 56 Dec 25, 2022
Remix enables you to build fantastic user experiences for the web and feel happy with the code that got you there. In this workshop, we'll look at some more advanced use cases when building Remix applications.

?? Advanced Remix Workshop Remix enables you to build fantastic user experiences for the web and feel happy with the code that got you there. In this

Frontend Masters 167 Dec 9, 2022
Remix enables you to build fantastic user experiences for the web and feel happy with the code that got you there. Get a jumpstart on Remix with this workshop.

?? Remix Fundamentals Build Better websites with Remix Remix enables you to build fantastic user experiences for the web and feel happy with the code

Frontend Masters 204 Dec 25, 2022
simple-remix-blog is a blog template built using Remix and TailwindCSS. Create your own blog in just a few minutes!

simple-remix-blog is a blog template built using remix.run and TailwindCSS. It supports markdown and MDX for the blog posts. You can clone it and star

José Miguel Álvarez Vañó 8 Dec 8, 2022
GetOsLocalesCrossPlatform - A cross platform alternative to get locales used on the platform. Works on Node, Electron, NW.js and Browsers

getOsLocalesCrossPlatform A cross platform alternative to get locales used on the platform. Works on Node, Electron, NW.js and Browsers This script is

null 1 Jan 2, 2022
An opensource 360° media viewer written in JavaScript using Electron and Marzipano

open360viewer open360viewer is an opensource 360° media viewer. It is based on electron and marzipano. It currently supports opening equirectangular 3

null 4 Oct 9, 2022
Homepage of the Vim Inspired Electron Browser - Vieb

vieb.dev Vieb is the Vim Inspired Electron Browser. This repo contains the full source code of Vieb's website/homepage. This does not contain the sour

Jelmer van Arnhem 5 Dec 15, 2022
A Minecraft launcher written in Rust, with an Electron frontend.

RustMine A Minecraft launcher written in Rust, with an Electron frontend. Why? I thought it would be fun to use two of my most familiar languages, Rus

sussyimpostor 1 Oct 16, 2022
📗 A simple electron app to connect with Platzi and add a discord rich presence

Platzi - Discord Rich Presence RPC Electron Requirements Nodejs you can download the latest version -> here clone the repository like this -> git clon

Jonathan Dyallo 9 Oct 31, 2022
Unofficial WhatsApp Linux client built with Electron.

WhatsApp Desktop for Linux (unofficial) WhatsApp Linux client built with Electron. As WhatsApp doesn't compile the official app for Linux, here is an

Alberto Mimbrero 39 Jan 3, 2023
An Electron app using NAPI-RS and a native Rust module to implement a Svelte store.

Svelte Store Written in Rust This repo shows a demo of an Electron app running a Svelte UI that reads and writes a Svelte store, but with a twist: the

Dave Ceddia 27 Sep 20, 2022
Titlebar template for Electron-based desktop apps

Electron-Titlebar-Template CSS based MacOs UI Titlebar Template for Electron-based desktop apps Titlebar can: minimize maximize fullscreen close You c

null 3 May 18, 2022
💅 A ready-to-go with a well-thought-out structure Electron app boilerplate with ReactJS, TypeScript, CSS / SASS modules, SWC, Eslint, Prettier, GitHub Action releases and more.

Electron App ??  A ready-to-go with a well-thought-out structure Electron app boilerplate with ReactJS, TypeScript, CSS / SASS modules, SWC, Eslint, P

Dalton Menezes 155 Dec 29, 2022