LogoRobo.js

Frontend

Client-side development for Discord Activities

Activity templates use Vite (a fast build tool for web projects) for fast builds and hot module replacement (HMR) — when you save a file, changes appear in your browser immediately without a full page refresh. Frontend code lives in src/app/.

VS Code editor with a Robo.js activity project open, showing Activity.tsx with JSX code and the file explorer displaying src/app, src/api, and config directories

FocusThe editor showing Activity.tsx and the project structure in the sidebarZoom100%NotesShow VS Code with file explorer displaying src/app (App.tsx, Activity.tsx), src/api (token.ts), src/hooks, config, and public. Activity.tsx open with JSX content.

Project Layout

Activity projects organize frontend code into a few key directories:

App.tsxEntry point
Activity.tsxMain activity view
  • src/app/App.tsx — Entry point that wraps providers and renders the main component.
  • src/app/Activity.tsx — Main UI component for your activity.
  • src/hooks/ — Custom hooks like useDiscordSdk for interacting with the Embedded App SDK.
  • public/ — Static assets served at the root path.

Vite Integration

Robo.js integrates Vite as the frontend build tool for activity projects. The configuration file is config/vite.mjs:

config/vite.mjs
import { DiscordProxy } from '@robojs/patch'
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    react(), // Fast JSX compilation using SWC
    DiscordProxy.Vite() // Rewrites network requests to work through Discord's proxy
  ],
  server: {
    allowedHosts: true // Allows tunnel URLs to connect to the dev server
  }
})
config/vite.mjs
import { DiscordProxy } from '@robojs/patch'
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    react(), // Fast JSX compilation using SWC
    DiscordProxy.Vite() // Rewrites network requests to work through Discord's proxy
  ],
  server: {
    allowedHosts: true // Allows tunnel URLs to connect to the dev server
  }
})

Running npx robo dev starts the Vite dev server alongside the Robo backend.

Hot Module Replacement

Changes to files in src/app/ hot reload instantly in the browser. Component state is preserved where possible, so you can iterate on UI without losing context. No manual refresh is needed.

Split view showing code editor on the left with a component being edited and browser on the right reflecting changes instantly through HMR

FocusSide-by-side code changes and their instant reflection in the browserZoom100%NotesShow split-screen: left has Activity.tsx being modified (changing a heading), right has browser at localhost showing the updated result. Demonstrates HMR.

Styling

CSS files in src/app/ are imported directly in components:

src/app/App.tsx
import './App.css'
src/app/App.jsx
import './App.css'

CSS Modules are supported out of the box. Name your file with the .module.css extension and import it as an object:

src/app/Activity.tsx
import styles from './Activity.module.css'
src/app/Activity.jsx
import styles from './Activity.module.css'

Tailwind CSS integration is available through the react-tailwind-ts template. To add Tailwind to an existing project, follow the Vite guide in the Tailwind CSS docs.

Static Assets

Files placed in the public/ directory are served at the root path. Reference them with absolute paths:

src/app/Activity.tsx
<img src="/rocket.png" alt="Rocket" />
src/app/Activity.jsx
<img src="/rocket.png" alt="Rocket" />

Store images, fonts, and other static files here.

React vs Vanilla

ReactVanilla
Plugin compatibilityFull (@robojs/sync, etc.)Limited (some plugins like @robojs/sync require React hooks)
Component modelJSX componentsManual DOM
Bundle sizeLargerMinimal
Templatereact-ts (default)vanilla-ts

Custom Vite Configuration

Add plugins, aliases, and build options by editing config/vite.mjs. For example, adding a path alias:

config/vite.mjs
import { DiscordProxy } from '@robojs/patch'
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from 'vite'
import path from 'node:path'

export default defineConfig({
  plugins: [react(), DiscordProxy.Vite()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '../src')
    }
  },
  server: {
    allowedHosts: true
  }
})
config/vite.mjs
import { DiscordProxy } from '@robojs/patch'
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from 'vite'
import path from 'node:path'

export default defineConfig({
  plugins: [react(), DiscordProxy.Vite()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '../src')
    }
  },
  server: {
    allowedHosts: true
  }
})

DiscordProxy.Vite() must remain in the plugins array for proxy patching to work inside Discord.

Next Steps

On this page