A macOS-style desktop environment for React. Draggable windows, desktop icons, dark/light theming, and more.
- Declarative Configuration - Define your entire desktop with a single config object
- Two Window Types -
app(render components) andbrowser(render iframes) - Dark/Light Theming - Built-in theme support with smooth crossfade wallpaper transitions
- Draggable Windows - Full drag-and-drop support with title bar
- Resizable Windows - Resize from edges and corners
- Window Snapping - Snap to edges, split screen, and maximize
- Desktop Icons - Draggable icons linked to windows with minimize animations
- Mobile Responsive - Auto-maximize windows on small screens
- TypeScript - Full type safety with comprehensive interfaces
# Clone the template
git clone https://github.com/renatoworks/desktop-ui.git my-desktop
cd my-desktop
# Install dependencies
npm install
# Start development server
npm run devOpen http://localhost:5173 to see the demo.
desktop-ui/
├── src/
│ ├── components/ # Desktop, Window, DesktopIcon
│ ├── contexts/ # DesktopContext, ThemeContext
│ ├── hooks/ # useWindowManager
│ ├── types/ # TypeScript interfaces
│ ├── App.tsx # Demo application
│ ├── main.tsx # Entry point
│ └── index.ts # Library exports
├── index.html
├── vite.config.ts
└── tailwind.config.js
Edit src/App.tsx to customize your desktop:
import { Desktop, DesktopConfig } from './';
function MyApp() {
return (
<div className="h-full bg-black text-white p-4">
<h1>Hello World!</h1>
</div>
);
}
const config: DesktopConfig = {
windows: [
{
id: 'my-app',
type: 'app',
title: 'My App',
component: MyApp,
},
],
icons: [
{
id: 'my-app-icon',
windowId: 'my-app',
icon: <MyIcon />,
label: 'My App',
},
],
darkBackground: {
type: 'gradient',
gradient: 'linear-gradient(to bottom, #1a1a2e, #16213e)',
},
};
export default function App() {
return <Desktop config={config} />;
}interface DesktopConfig {
windows: WindowConfig[]; // Array of window configurations
icons: DesktopIconConfig[]; // Array of desktop icons
background?: BackgroundConfig; // Desktop background (fallback)
darkBackground?: BackgroundConfig; // Background for dark theme
lightBackground?: BackgroundConfig;// Background for light theme
iconLayout?: IconLayoutConfig; // How icons are arranged
autoOpenWindow?: string; // Window ID to open on mount
}{
id: 'terminal',
type: 'app',
title: 'Terminal',
icon: <TerminalIcon />,
component: TerminalApp,
componentProps: { theme: 'dark' }, // Props passed to component
dimensions: {
responsive: false,
width: '800px',
height: '600px',
initialX: 100,
initialY: 100,
},
openMaximized: false, // Open maximized on desktop
canMinimize: true,
canMaximize: true,
canResize: true,
}{
id: 'browser',
type: 'browser',
title: 'https://example.com',
url: 'https://example.com',
showReloadButton: true,
openMaximized: true, // Great for showcasing websites
}{
id: 'terminal-icon',
windowId: 'terminal', // Links to window by ID
icon: <TerminalIcon />, // React element or string
label: 'Terminal',
}// Solid color
{ type: 'color', color: '#1a1a2e' }
// Image
{ type: 'image', url: '/wallpaper.jpg', size: 'cover', position: 'center' }
// Gradient
{ type: 'gradient', gradient: 'linear-gradient(to bottom, #1a1a2e, #16213e)' }Use darkBackground and lightBackground for theme-aware wallpapers with crossfade transitions:
const config: DesktopConfig = {
darkBackground: {
type: 'image',
url: '/wallpapers/night.jpg',
size: 'cover',
},
lightBackground: {
type: 'image',
url: '/wallpapers/day.jpg',
size: 'cover',
},
// ... windows and icons
};Access theme state from any component inside the Desktop:
import { useTheme } from './';
function SettingsPanel() {
const { isDark, toggleTheme, colors } = useTheme();
return (
<div style={{ backgroundColor: colors.overlayBg }}>
<button onClick={toggleTheme}>
{isDark ? 'Light Mode' : 'Dark Mode'}
</button>
</div>
);
}| Token | Description |
|---|---|
windowBg |
Window background |
titleBarBg |
Active title bar |
titleBarBgInactive |
Inactive title bar |
border |
Border color |
textPrimary |
Primary text |
textSecondary |
Muted text |
urlBarBg |
Browser URL bar |
hoverBg |
Hover state |
overlayBg |
Overlay/loading |
- Top edge: Maximize to full screen
- Left/Right edge: Snap to half screen
- Windows auto-maximize below 768px width
- Resize handles hidden on touch devices
- Windows animate to/from their icon position
import { useDesktopContext, useTheme } from './';
function MyComponent() {
const { openWindow, closeWindow, windows } = useDesktopContext();
const { isDark, toggleTheme, colors } = useTheme();
// ...
}If you need SSR, API routes, or prefer Next.js, follow these steps:
npx create-next-app@latest my-desktop --typescript --tailwind --app
cd my-desktop# Copy from desktop-ui to your Next.js project
cp -r desktop-ui/src/components ./src/
cp -r desktop-ui/src/contexts ./src/
cp -r desktop-ui/src/hooks ./src/
cp -r desktop-ui/src/types ./src/
cp desktop-ui/src/index.ts ./src/// app/page.tsx
'use client';
import { Desktop } from '@/';
import type { DesktopConfig } from '@/';
const config: DesktopConfig = {
// ... your config
};
export default function Page() {
return <Desktop config={config} />;
}// tailwind.config.ts
content: [
'./src/**/*.{js,ts,jsx,tsx}',
'./app/**/*.{js,ts,jsx,tsx}',
],/* app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
html, body {
height: 100%;
overflow: hidden;
}| Vite | Next.js |
|---|---|
No 'use client' needed |
Add 'use client' to pages using Desktop |
src/main.tsx entry |
app/page.tsx entry |
index.html |
Next.js handles HTML |
| Client-side only | Can use SSR for other pages |
MIT
Renato Costa (renato.works)
Made in Blueberry 🫐
