Building a Web Browser with React and Electron

Building a Web Browser with React and Electron

Web browsers are essential tools for navigating the web. While there are many great web browsers available, what if you could create your custom browser tailored to your specific needs and preferences? With electron-vite, we can do just that.

Electron-vite aims to provide a faster and leaner development experience for Electron. It combines the capabilities of Electron, a framework for building cross-platform desktop applications, with the simplicity and speed of Vite, a front-end build tool.

Using electron-vite, we will create a minimalist web browser in under an hour.

View Github Repo

Get Started

To get started, you must have Node.js and npm installed on your system. Once you have Node.js and npm installed, you can create a new Electron-Vite project by running the following command in your terminal:

npm create @quick-start/electron@latest

// Follow the prompts to choose a project name, framework, and additional options. The following are the options I selected.

? Project name: prospect
? Select a framework: react
? Add TypeScript? Yes
? Add Electron updater plugin? No
? Enable Electron download mirror proxy? No        

Select a UI Framework

After scaffolding your project with Electron and React, you should choose a UI Library to minimize the amount of work required to design the browser's interface. I picked Bootstrap and used the Cerulean theme from Bootswatch. You can use React Bootstrap, but I am faster at prototyping with vanilla Bootstrap.

Requirements

Before beginning this endeavor, let's have a clear set of requirements that we can use to ensure the proper functionality of our web browser.

As a user of the web browser, I should be able to:

  • Visit a website;
  • Navigate a website;
  • and move the browser window around my desktop.

Customize Electron Window

Electron gives you complete control over the appearance and behavior of your application's window. I can customize my browser's window size, position, title bar, and more.

I removed the window frame from my web browser to maximize it's minimalism. Check out src/main/index.tx and modify it to suit your needs (See GitHub Project).

You'll have to ensure that the webviewTag is set to true to ensure that webviews are supported in the application. This is required to display web pages.

  const mainWindow = new BrowserWindow({
    width: 900,
    height: 670,
    show: false,
    autoHideMenuBar: true,
    ...(process.platform === 'linux' ? { icon } : {}),
    webPreferences: {
      preload: join(__dirname, '../preload/index.js'),
      sandbox: false,
      webviewTag: true
    },
    frame: false
  })        

Create Components

The next step is to create the essential components for your browser, including an address bar and a splash screen.

AddressBar.tsx

This component will ensure users can browse the web and drag the browser window around their desktop.

import './styles.css'

const AddressBar = ({
  url,
  setUrl
}: {
  url: string
  setUrl: React.Dispatch<React.SetStateAction<string>>
}): JSX.Element => {
  return (
    <nav className="navbar bg-primary text-white p-2 border-bottom-dark sticky-top" id="drag">
      <div className="container">
        <input
          type="text"
          className="form-control border-primary shadow-sm"
          id="no-drag"
          value={url}
          onChange={(e) => setUrl(e.target.value)}
        />
      </div>
    </nav>
  )
}

export default AddressBar
        

I assigned the nav element an ID of drag ensure the user can drag the window around without a title bar and the input element an ID of no-drag to ensure the user can activate the field. Next, I created a styles.css to assign the correct -webkit-app-region style.

#drag {
  -webkit-app-region: drag;
}

#no-drag {
  -webkit-app-region: no-drag;
}
        

For additional details, See the Electron Docs.

Splash.tsx

This will be visible when the user has an empty URL. This will ensure the user has a pleasant experience

import ProspectIcon from '../assets/prospect.png'

const Splash = (): JSX.Element => {
  return (
    <div className="p-5" id="drag">
      <img src={ProspectIcon} alt="" style={{ maxWidth: 250 }} />
      <h1>Prospect</h1>
      <p>0.0.1</p>
    </div>
  )
}

export default Splash
        

Setup App.tsx

Now that we've created the essential components for our web browser, we need to tie it all together in App.tsx

import { useRef, useState } from 'react'
import AddressBar from './components/AddressBar'
import Splash from './components/Splash'

function App(): JSX.Element {
  const [url, setUrl] = useState<string>('')
  const webviewRef = useRef<HTMLWebViewElement>(null)

  return (
    <>
      <AddressBar setUrl={setUrl} url={url} />
      {url ? (
        <webview
          ref={webviewRef}
          src={`${url.includes('https://') ? '' : 'https://'}${url}`}
        ></webview>
      ) : (
        <Splash />
      )}
    </>
  )
}

export default App
        


Run the Application

To get the application running, run the following command.

npm run dev        

This will open the browser window in development.

As you can see, there is an issue with the way our browser is rendering the webpage. This is because no styling for the webview tag was included. To resolve this issue, I created a custom hook that we can use to measure the window dimensions, calculate the height required for the webview, and prevent any overflows from occurring by considering the height of the address bar.

useWindowDimensions

import { useEffect, useState } from 'react'

type WindowDimensions = {
  width: number
  height: number
}

const getWindowDimensions = (): WindowDimensions => {
  const { innerWidth: width, innerHeight: height } = window
  return {
    width,
    height
  }
}

const useWindowsDimensions = (): WindowDimensions => {
  const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions())

  useEffect(() => {
    const handleResize = (): void => {
      setWindowDimensions(getWindowDimensions())
    }

    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])

  return windowDimensions
}

export default useWindowsDimensions        

With this custom hook in place, let's update App.tsx to use it and resolve this ugly issue.

import { useRef, useState } from 'react'
import './assets/styles.css'
import AddressBar from './components/AddressBar'
import useWindowsDimensions from './hooks/useWindowsDimensions'
import Splash from './components/Splash'

function App(): JSX.Element {
  const [url, setUrl] = useState<string>('')
  const windowDimensions = useWindowsDimensions()
  const webviewRef = useRef<HTMLWebViewElement>(null)

  return (
    <>
      <AddressBar setUrl={setUrl} url={url} />
      {url ? (
        <webview
          ref={webviewRef}
          src={`${url.includes('https://') ? '' : 'https://'}${url}`}
          style={{
            // height of address bar is 54 pixels
            height: windowDimensions.height - 54 
          }}
        ></webview>
      ) : (
        <Splash />
      )}
    </>
  )
}

export default App
        
It worked!

The Result

After following the instructions above, you've created your own web browser!

Want to check out Prospect, the browser built here? Check out the GitHub Repo.

Jonatha L Teixeira

Graphic Designer | Visual Designer | Learning Designer | Storyline & Rise Developer | Instructional Design | eLearning Developer | edTech | Multidisciplinary Professional

4 天前

Wow! Your tutorial is really clear and direct, and there's something I particularly liked: you tested the app and showed that something was missing to correct the webpage rendering. After that, you explained how to implement the solution. The way you organized the article aligns with how I like to think, too! I'll be following your content from now on. ??

Jonatha L Teixeira

Graphic Designer | Visual Designer | Learning Designer | Storyline & Rise Developer | Instructional Design | eLearning Developer | edTech | Multidisciplinary Professional

4 天前

Hey, Miguel R.! Do you know any group (Telegram or Linkedin) where people share knowledge about React and Electron? I just start to learn React and I'm in love for it. As a self-taught learning, I started a project and I'm having some issues with the react build feature (this doesn't working. It builds, but doesn't load the app). A group where we can share these kind of things will be helpful to me. Thanks!

回复
Vikram Shetty ??

The ROI Guy ? I help DEI Consultants get more warm leads ? Download my ROI of DEI white paper to learn the framework (see featured section)

8 个月

Speed in development leads to quicker feedback loops, enhancing agility and efficiency in building innovative solutions. P.S.?Insightful post

要查看或添加评论,请登录

社区洞察

其他会员也浏览了