<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dmytro</title>
    <description>The latest articles on DEV Community by Dmytro (@cebastion).</description>
    <link>https://dev.to/cebastion</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3737117%2F3b737456-4d63-4c7a-adb6-fe0431dc77af.jpeg</url>
      <title>DEV Community: Dmytro</title>
      <link>https://dev.to/cebastion</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cebastion"/>
    <language>en</language>
    <item>
      <title>Devlog #3 --- exchange data between Electron and React</title>
      <dc:creator>Dmytro</dc:creator>
      <pubDate>Sun, 01 Feb 2026 08:45:43 +0000</pubDate>
      <link>https://dev.to/cebastion/devlog-3-exchange-data-between-electron-and-react-2639</link>
      <guid>https://dev.to/cebastion/devlog-3-exchange-data-between-electron-and-react-2639</guid>
      <description>&lt;p&gt;Hello everyone!&lt;/p&gt;

&lt;p&gt;Today, I want to talk about the relationship between Electron and React. This is a very important topic, because you usually exchange data between them.&lt;/p&gt;

&lt;p&gt;I was using React in Electron Forge, and for my extension, I need to get data from the system and also send a signal to replay the animation.&lt;/p&gt;




&lt;h4&gt;
  
  
  How to work with data exchange between Electron and React?
&lt;/h4&gt;

&lt;p&gt;So, they exchange data with the help of IPC (Inter-process Communication). They have two types of modules, "ipcMain" and "ipcRender". The first module is used in the main file (index.ts). The second one is used in "preload.ts". To make a connection between them, a channel is used, which you can name whatever you want. This communication can analogous to a Socket, which is also used to channel/event to help exchange data.&lt;/p&gt;




&lt;h4&gt;
  
  
  How to make it in code?
&lt;/h4&gt;

&lt;p&gt;Well, the first step is to choose a communication pattern. The official site proposes 4 patterns. I consider a pattern called "Main to renderer". Other patterns you can read &lt;strong&gt;at&lt;/strong&gt; this &lt;a href="https://www.electronjs.org/docs/latest/tutorial/ipc" rel="noopener noreferrer"&gt;link&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;So, let's consider my code in the file "index.ts", which I implemented to send data about MEM, CPU, and Disk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const sendData = () =&amp;gt; {
  for (const [key, value] of Object.entries(funcs)) {
    mainWindow.webContents.send(key, value());
  }
}

const createWindow = (): void =&amp;gt; {
  mainWindow = new BrowserWindow({
    height: 200,
    width: 400,
    frame: false,
    transparent: true,
    show: false,
    resizable: false,
    alwaysOnTop: true,
    skipTaskbar: true,
    roundedCorners: true,
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
  });

  mainWindow.on('blur', () =&amp;gt; {
    mainWindow?.hide();
  });

  mainWindow.webContents.on('before-input-event', (event, input) =&amp;gt; {
    if (input.key === 'Escape') {
      mainWindow?.hide();
      event.preventDefault();
    }
  });

  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
  setInterval(sendData, 1000)
};

app.whenReady().then(() =&amp;gt; {
  tray = new Tray(icon)

  setInterval(() =&amp;gt; {
    const data = MyNativeAddon.watchTray();
    console.log(data);
  }, 1000)

  tray.on("click", () =&amp;gt; {
    if (!mainWindow) {
      createWindow();
    }

    if (!position) {
      const { x, y } = positioner.calculate(mainWindow.getBounds(), tray.getBounds())
      position = { x, y }
    }

    if (mainWindow?.isVisible()) {
      mainWindow.hide();
      mainWindow.webContents.send('stop-animation');
    } else {
      mainWindow.setAlwaysOnTop(true, "pop-up-menu");
      mainWindow.setPosition(position.x, position.y + 5);
      mainWindow.show();
      mainWindow.focus();
      mainWindow.webContents.send('start-animation');
    }
  });
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ask you to notice this part of the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; mainWindow.webContents.send(key, value());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we create a channel on the main side using "ipcMain". Where key is the channel name, after that a value - it's a callback function. The value is not a required parameter. &lt;/p&gt;

&lt;p&gt;Then, we need to create the same channel in "preload.ts":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { contextBridge, ipcRenderer } from "electron";
import funcs from "./objects/funcs.object";

type Callback = (data: any) =&amp;gt; void;

const exposedFuncs: Record&amp;lt;
  string,
  (callback: Callback) =&amp;gt; void
&amp;gt; = {};

for (const [key] of Object.entries(funcs)) {
  exposedFuncs[key] = (callback: Callback) =&amp;gt; {
    ipcRenderer.on(key, (_event, data) =&amp;gt; callback(data));
  };
}

contextBridge.exposeInMainWorld('bridge', {
  ...exposedFuncs,
    startAnimation: (cb: () =&amp;gt; void) =&amp;gt; {
    ipcRenderer.on("start-animation", cb);
  },
  stopAnimation: (cb: () =&amp;gt; void) =&amp;gt; {
    ipcRenderer.on("stop-animation", cb);
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, here we use contextBridge, which uses the &lt;code&gt;exposeInMainWorld&lt;/code&gt; method and is written with a similar name as in "ipcMain" and similar key and callback.&lt;/p&gt;

&lt;p&gt;Also, if you use TypeScript, you must create a &lt;code&gt;&amp;lt;file_name&amp;gt;.d.ts&lt;/code&gt; file and write the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export { };

declare global {
  interface Window {
    bridge: {
      getMemory: GetMemory;
      getCPU: GetCPU;
      getDisk: GetDisk;
    };
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How to call methods in React from Electron?
&lt;/h4&gt;

&lt;p&gt;After that, when we create a "bridge", we can call methods in React from Electron:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState, useEffect } from "react";

const useSystem = () =&amp;gt; {
  const [memory, setMemory] = useState(null);
  const [cpu, setCpu] = useState(null);
  const [disk, setDisk] = useState(null);

  useEffect(() =&amp;gt; {
    window.bridge.getMemory(data =&amp;gt; setMemory(data));
    window.bridge.getCPU(data =&amp;gt; setCpu(data));
    window.bridge.getDisk(data =&amp;gt; setDisk(data));
  }, [memory, cpu, disk]);

  return { memory, cpu, disk };
}

export default useSystem

---------------------------------------------------------------------------------

import { useState, useEffect } from "react"

const useVisible = () =&amp;gt; {
  const [visible, setVisible] = useState(false)

  useEffect(() =&amp;gt; {
    window.bridge.startAnimation(() =&amp;gt; setVisible(true))
    window.bridge.stopAnimation(() =&amp;gt; setVisible(false))
  }, [])

  return visible
}

export default useVisible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, I used custom hooks for more comfort. But you can notice that I call functions via window... We can say that we expand the Window API which calls Renderer (our the window..) -&amp;gt; Preload -&amp;gt; Main -&amp;gt; Renderer.&lt;/p&gt;

&lt;p&gt;In general, today we considered how to create signals between Electron and React in my example code. I hope this devlog helps other beginner developers.&lt;/p&gt;

&lt;p&gt;Thanks to all for your attention! I hope you enjoyed reading my blog!&lt;br&gt;&lt;br&gt;
Have a good day!&lt;/p&gt;

&lt;p&gt;Additionally, you can read about it on the official Electron website:&lt;br&gt;
&lt;a href="https://www.electronjs.org/docs/latest/api/web-contents" rel="noopener noreferrer"&gt;https://www.electronjs.org/docs/latest/api/web-contents&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.electronjs.org/docs/latest/api/ipc-renderer" rel="noopener noreferrer"&gt;https://www.electronjs.org/docs/latest/api/ipc-renderer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Devlog #2 — how to connect TailwindCSS to Electron Forge?</title>
      <dc:creator>Dmytro</dc:creator>
      <pubDate>Fri, 30 Jan 2026 05:00:13 +0000</pubDate>
      <link>https://dev.to/cebastion/devlog-2-how-to-connect-tailwindcss-to-electron-forge-46ea</link>
      <guid>https://dev.to/cebastion/devlog-2-how-to-connect-tailwindcss-to-electron-forge-46ea</guid>
      <description>&lt;p&gt;Hello everybody!&lt;/p&gt;

&lt;p&gt;Today I want to talk about TailwindCSS and Electron Forge, and how to connect them.&lt;/p&gt;

&lt;p&gt;TailwindCSS is a pretty strong library for building UI. Personally, I think it’s a perfect solution, because you don’t need to create separate CSS or SCSS files and write styles for every single element. &lt;/p&gt;




&lt;h3&gt;
  
  
  Installing Tailwind CSS in an Electron Forge project
&lt;/h3&gt;

&lt;p&gt;First, we need to install our packages for TailwindCSS:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install tailwindcss @tailwindcss/postcss postcss&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then we create a file called &lt;strong&gt;"postcss.config.cjs"&lt;/strong&gt;, where we write this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  plugins: {
    "@tailwindcss/postcss": {},
    autoprefixer: {},
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, don't forget about &lt;strong&gt;"tailwind.config.js"&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
    "./public/index.html"
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we created a connection between TailwindCSS and Electron Forge! All that’s left is to import Tailwind CSS in index.css and make a wonderful design!&lt;/p&gt;

&lt;p&gt;By the way, I'm going to make a little announcement. The next devlog will be about data exchange and signals between Electron and React. It's a very interesting and necessary topic.&lt;/p&gt;

&lt;p&gt;Thanks to all for your attention! I hope you enjoyed reading my blog!&lt;br&gt;&lt;br&gt;
Have a good day!&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>electron</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Devlog #1 — Introduction started an extension for Linux</title>
      <dc:creator>Dmytro</dc:creator>
      <pubDate>Wed, 28 Jan 2026 11:58:38 +0000</pubDate>
      <link>https://dev.to/cebastion/devlog-1-introduction-started-an-extension-for-linux-3gld</link>
      <guid>https://dev.to/cebastion/devlog-1-introduction-started-an-extension-for-linux-3gld</guid>
      <description>&lt;p&gt;Hello everyone, and welcome to my first devlog!&lt;/p&gt;

&lt;p&gt;Today, I want to talk about my project, which is called "Pulse". It’s an extension for different Linux distributions. For now, it’s a simple panel that you can monitor memory, CPU, and disk usage, but later I plan to add more interesting features. &lt;/p&gt;

&lt;p&gt;This extension will be created with Electron Forge, using TypeScript, React, Framer Motion, and Tailwind CSS.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why did I choose Electron Forge, rather than GJS?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So, I tried creating an extension via GJS, but I couldn’t achieve what I wanted. In my opinion, GJS has complicated documentation and doesn't provide the necessary tools for creating an extension, which I want to do. Undoubtedly, GJS is more lightweight and native to Linux, but it’s limited to GNOME, while I aim to support all distributions of Linux.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Creating a logo&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Also, I decided to create a logo for my extension today. You can see it below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbdy3hov0cx6p14okd8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbdy3hov0cx6p14okd8v.png" alt=" " width="495" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks like a robot. Overall, I like the result, but generally I planned to make eyes that watch a computer. In the end, I managed to do it.&lt;/p&gt;

&lt;p&gt;Thanks to all for your attention! I hope you enjoyed reading my first blog!&lt;br&gt;
Have a good day!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>extensions</category>
      <category>electron</category>
    </item>
  </channel>
</rss>
