1. Your First App
Build a hello world desktop app with LightShell from scratch.
In this tutorial, you will create a simple desktop app, run it in development mode, and see live changes. By the end, you will understand the basic LightShell project structure and workflow.
Scaffold the Project
Section titled “Scaffold the Project”Open your terminal and run:
lightshell init hello-worldcd hello-worldThis creates a project with three files in src/ and a configuration file.
Explore the Project Structure
Section titled “Explore the Project Structure”lightshell.json
Section titled “lightshell.json”This is your app’s configuration file:
{ "name": "hello-world", "version": "1.0.0", "entry": "src/index.html", "window": { "title": "Hello World", "width": 1024, "height": 768, "minWidth": 400, "minHeight": 300, "resizable": true, "frameless": false }, "build": { "icon": "assets/icon.png", "appId": "com.example.hello-world" }}Key fields:
- entry — the HTML file loaded when the app starts
- window — initial window dimensions and behavior
- build — packaging settings like the app icon and identifier
src/index.html
Section titled “src/index.html”The entry point for your app:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hello World</title> <link rel="stylesheet" href="style.css"></head><body> <h1>Hello, LightShell!</h1> <p>Edit src/index.html and save to see changes.</p> <script src="app.js"></script></body></html>This is standard HTML. Nothing special about it. LightShell injects its runtime scripts automatically before your code runs, so window.lightshell is available in app.js without any imports.
src/app.js
Section titled “src/app.js”Your application logic:
// All lightshell APIs are available via window.lightshell// They are all async — always use await
async function init() { const platform = await lightshell.system.platform() console.log(`Running on ${platform}`)}
init()src/style.css
Section titled “src/style.css”Standard CSS. LightShell injects a normalization layer before your styles, so form elements and scrollbars look consistent across macOS and Linux.
Run the App
Section titled “Run the App”lightshell devA native window opens showing your HTML. The window title, size, and other properties come from lightshell.json.
Make Changes
Section titled “Make Changes”With the app still running, open src/index.html in your editor. Change the heading:
<h1>My First Desktop App</h1>Save the file. The app reloads automatically — you should see the new heading within a fraction of a second. This is hot reload in action. It watches every file in src/ and triggers a refresh when anything changes.
Add Interactivity
Section titled “Add Interactivity”Edit src/app.js to add a button that shows the current platform:
async function init() { const platform = await lightshell.system.platform() const arch = await lightshell.system.arch() const hostname = await lightshell.system.hostname()
document.body.innerHTML = ` <h1>My First Desktop App</h1> <p>Platform: ${platform} (${arch})</p> <p>Hostname: ${hostname}</p> <button id="greet">Say Hello</button> <div id="output"></div> `
document.getElementById('greet').addEventListener('click', async () => { const homeDir = await lightshell.system.homeDir() document.getElementById('output').textContent = `Hello from ${homeDir}!` })}
init()Save and watch the app update. Click the button to see your home directory path displayed — this is a native API call, not available in a regular browser.
You have learned how to:
- Scaffold a project with
lightshell init - Run the development server with
lightshell dev - Edit files and see hot reload in action
- Call native APIs from JavaScript using
window.lightshell
Next, we will explore the native APIs in depth.