Skip to content
Back to skills

Electron Fsd

ASecurity

This skill should be used when developing Electron applications with Feature-Sliced Design (FSD) architecture and React 19. Triggers on requests to create components, features, entities, widgets, or pages following FSD layer structure. Also applies when setting up Electron main/preload/renderer process code organization.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
developmenttypescriptreactnodeapidocumentation

Works with

  • cli
  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill electron-fsd --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Electron Fsd?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Electron Fsd
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-electron-fsd/badge)](https://www.skillsdirectory.com/skills/majiayu000-electron-fsd)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: electron-fsd
description: This skill should be used when developing Electron applications with Feature-Sliced Design (FSD) architecture and React 19. Triggers on requests to create components, features, entities, widgets, or pages following FSD layer structure. Also applies when setting up Electron main/preload/renderer process code organization.
---

# Electron FSD Development (React 19)

## Overview

This skill provides guidance for developing Electron applications using Feature-Sliced Design (FSD) architecture with React 19. It covers the three-process model (Main, Preload, Renderer) and FSD layer organization for the Renderer process.

## Architecture Patterns

### Electron Process Model

```
┌─────────────────────────────────────────────────────────────┐
│  Main Process (Node.js)                                     │
│  ├─ app/main.ts         - App lifecycle, BrowserWindow      │
│  ├─ app/ipc-handler.ts  - IPC routing to services           │
│  ├─ services/           - Business logic (PdfMergeService)  │
│  └─ workers/            - CPU-intensive tasks               │
├─────────────────────────────────────────────────────────────┤
│  Preload (Isolated)                                         │
│  └─ index.ts            - ContextBridge (window.api)        │
├─────────────────────────────────────────────────────────────┤
│  Renderer (React + FSD)                                     │
│  ├─ shared/             - Common utilities, UI, types       │
│  ├─ entities/           - READ (data display)               │
│  ├─ features/           - CUD (user actions)                │
│  ├─ widgets/            - Feature composition               │
│  └─ pages/              - Route entry points                │
└─────────────────────────────────────────────────────────────┘
```

### FSD Layer Responsibilities

| Layer | Responsibility | IPC Direction | Example |
|-------|----------------|---------------|---------|
| `entities` | Data display only | READ | `document-card.tsx`, `page-thumbnail.tsx` |
| `features` | User actions | CUD | `use-merge-command.ts`, `merge-toolbar.tsx` |
| `widgets` | Feature composition | - | `merge-workspace.tsx` |
| `shared` | Cross-cutting concerns | - | `ipc-client.ts`, types, UI components |

## Component Creation Workflow

### Creating an Entity (READ-only)

To create a new entity for displaying data:

1. Create entity folder: `src/renderer/entities/{entity-name}/`
2. Structure:
```
entities/pdf-document/
├─ ui/
│  └─ document-card.tsx     # Display component
└─ model/
   └─ use-pdf-metadata.ts   # Data fetching hook (READ)
```

3. Entity components must:
   - Only display data (no mutations)
   - Use hooks for data fetching
   - Accept data via props or context

### Creating a Feature (CUD operations)

To create a new feature for user actions:

1. Create feature folder: `src/renderer/features/{feature-name}/`
2. Structure:
```
features/pdf-merge/
├─ ui/
│  ├─ merge-toolbar.tsx      # Action UI
│  └─ merge-file-grid.tsx    # Interactive grid with DnD
└─ model/
   └─ use-merge-command.ts   # IPC command hook
```

3. Feature components must:
   - Handle user interactions
   - Call IPC commands for mutations
   - Compose entities for display

### Creating a Widget (Composition)

To compose features and entities into workspace:

1. Create widget folder: `src/renderer/widgets/{widget-name}/`
2. Structure:
```
widgets/merge-workspace/
└─ merge-workspace.tsx
```

3. Widget implementation:
```typescript
// merge-workspace.tsx
export function MergeWorkspace() {
  const { files, addFiles, removeFile } = useMergeContext();

  return (
    <>
      <MergeToolbar onAddFiles={addFiles} />
      <MergeFileGrid files={files} onRemove={removeFile} />
    </>
  );
}
```

## File Naming Conventions

| Element | Convention | Example |
|---------|------------|---------|
| Folders & files | kebab-case | `pdf-merge-service.ts` |
| React components | PascalCase | `MergeWorkspace` |
| Hooks | camelCase with `use` prefix | `useMergeCommand` |
| Types/interfaces | PascalCase | `MergeRequest` |
| Services | PascalCase + Service | `PdfMergeService` |
| Workers | kebab-case + worker | `merge-worker.ts` |

## React 19 Component Patterns

### No forwardRef (React 19)

```typescript
// ❌ React 18 (forwardRef)
const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => (
  <input ref={ref} {...props} />
));

// ✅ React 19 (ref as regular prop)
interface InputProps {
  ref?: React.Ref<HTMLInputElement>;
  placeholder?: string;
}
function Input({ ref, ...props }: InputProps) {
  return <input ref={ref} {...props} />;
}
```

### Context as Provider (React 19)

```typescript
// ❌ React 18
<ThemeContext.Provider value={theme}>
  {children}
</ThemeContext.Provider>

// ✅ React 19
<ThemeContext value={theme}>
  {children}
</ThemeContext>
```

### use() Hook for Promise/Context

```typescript
// ✅ React 19: use() for Promise (requires Suspense)
import { use, Suspense } from 'react';

function FileList({ filesPromise }: { filesPromise: Promise<PdfFile[]> }) {
  const files = use(filesPromise);
  return files.map(f => <FileCard key={f.id} file={f} />);
}

// Usage with Suspense
<Suspense fallback={<Loading />}>
  <FileList filesPromise={fetchFiles()} />
</Suspense>

// ✅ React 19: use() for Context (conditional allowed)
function ConditionalTheme({ show }: { show: boolean }) {
  if (show) {
    const theme = use(ThemeContext);
    return <div className={theme}>Themed</div>;
  }
  return null;
}
```

### useActionState for Forms

```typescript
// ✅ React 19: Form action with state
import { useActionState } from 'react';

function MergeForm() {
  const [state, submitAction, isPending] = useActionState(
    async (prev, formData) => {
      const files = formData.getAll('files');
      const result = await window.api.merge(files);
      return result;
    },
    null
  );

  return (
    <form action={submitAction}>
      <input type="file" name="files" multiple disabled={isPending} />
      <button disabled={isPending}>
        {isPending ? 'Merging...' : 'Merge PDFs'}
      </button>
    </form>
  );
}
```

### useOptimistic for Optimistic UI

```typescript
// ✅ React 19: Optimistic updates
import { useOptimistic } from 'react';

function FileList({ files }: { files: PdfFile[] }) {
  const [optimisticFiles, addOptimistic] = useOptimistic(
    files,
    (state, newFile: PdfFile) => [...state, { ...newFile, pending: true }]
  );

  async function handleAdd(file: File) {
    const tempFile = { id: 'temp', name: file.name, pending: true };
    addOptimistic(tempFile);
    await window.api.addFile(file);
  }

  return (
    <div>
      {optimisticFiles.map(f => (
        <FileCard key={f.id} file={f} opacity={f.pending ? 0.5 : 1} />
      ))}
    </div>
  );
}
```

### ref Callback Cleanup

```typescript
// ✅ React 19: ref cleanup function
function AutoFocusInput() {
  return (
    <input
      ref={(el) => {
        if (el) el.focus();
        return () => {
          // cleanup when element removed
          console.log('Input unmounted');
        };
      }}
    />
  );
}
```

## Type Definition Rules

### No Inline Object Types

```typescript
// ❌ Avoid
function process(data: { id: string; name: string }) {}

// ✅ Preferred
interface ProcessData { id: string; name: string }
function process(data: ProcessData) {}
```

### Constants with ValueOf Pattern

```typescript
// Define constants
export const MERGE_STATUS = {
  IDLE: 'idle',
  PENDING: 'pending',
  COMPLETE: 'complete',
} as const;

// Create union type
type ValueOf<T> = T[keyof T];
type MergeStatus = ValueOf<typeof MERGE_STATUS>;

// Usage
const status: MergeStatus = MERGE_STATUS.PENDING;
```

## Resources

### references/
- `fsd-layers.md` - Detailed FSD layer documentation
- `electron-patterns.md` - Electron-specific patterns

To delete unused example files in `scripts/` and `assets/` directories as this skill focuses on architectural guidance.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.