Cosmo

Manual Setup

Learn how to convert an existing web project into a Cosmo widget.

While the create-cosmo-widget scaffolding tool is the easiest way to start, you can also manually configure an existing web project to work with Cosmo.

1. Install Dependencies

Install the Widget SDK for runtime type safety and utilities.

npm install @buildcosmo/widget
pnpm add @buildcosmo/widget
yarn add @buildcosmo/widget

If you're using Vite, you can optionally install the Cosmo Vite plugin for on-desktop dev widget testing:

npm install -D @buildcosmo/vite-plugin
pnpm add -D @buildcosmo/vite-plugin
yarn add -D @buildcosmo/vite-plugin

2. Configure Vite (Optional)

If you're using Vite, add the Cosmo plugin to your vite.config.js or vite.config.ts. This plugin handles configuration validation, asset packaging, and dev server communication for on-desktop testing.

vite.config.js
import { defineConfig } from "vite";
import { cosmo } from "@buildcosmo/vite-plugin";

export default defineConfig({
  plugins: [
    // ... other plugins (e.g., react(), vue())
    cosmo(),
  ],
});

Without the Vite plugin, you can still build and test your widget, but you won't have automatic on-desktop dev widget testing integration.

3. Add Widget Configuration

Create a widget.config.json file in the root of your project. This file is required for Cosmo to recognize and load your widget.

widget.config.json
{
  "minCosmoVersion": "0.4.0",
  "defaultWidth": 320,
  "defaultHeight": 200,
  "minWidth": 200,
  "minHeight": 120,
  "allowResize": true,
  "keepAspectRatio": false,
  "allowLockScreen": true,
  "allowInternet": false
}

See Widget Configuration for a full list of options.

4. Create Entry Point

Cosmo expects your widget to expose a global window.widget function. This function is called when the widget is initialized.

Modify your main entry file (e.g., src/main.jsx, src/main.ts, or src/main.js) to export this function.

src/main.js
function widget(preferences, widgetData) {
  const app = document.querySelector('#app');
  app.innerHTML = `<h1>Hello Cosmo!</h1>`;
}

window.widget = widget;

// Dev fallback: Simulate first load (widgetData is undefined)
if (import.meta.env.DEV && !window.webkit) {
  widget({ theme: "Default", hideBackground: false });
}
src/main.jsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

let root = null;

function widget(preferences, widgetData) {
  const container = document.getElementById('root');
  
  if (!root) {
    root = createRoot(container);
  }
  
  root.render(
    <StrictMode>
      <App preferences={preferences} initialData={widgetData} />
    </StrictMode>
  );
}

window.widget = widget;

// Dev fallback: Simulate first load (widgetData is undefined)
if (import.meta.env.DEV && !window.webkit) {
  widget({ theme: "Default", hideBackground: false });
}
src/main.ts
import { createApp } from 'vue';
import App from './App.vue';

function widget(preferences: Record<string, any>, widgetData?: Record<string, any>) {
  const app = createApp(App, {
    preferences,
    widgetData
  });
  app.mount('#app');
}

(window as any).widget = widget;

// Dev fallback: Simulate first load (widgetData is undefined)
if (import.meta.env.DEV && !(window as any).webkit) {
  widget({ theme: "Default", hideBackground: false });
}

Cosmo looks for window.widget. If this is not defined, your widget will display a blank screen.

5. Run Development Server

Start your development server as usual.

npm run dev

If you're using Vite with the Cosmo plugin and Cosmo's Developer Mode is enabled, it will automatically detect your local server and prompt you to open the widget on the desktop. Without the Vite plugin, you'll need to build your widget and load it manually for testing.