Switch React Menu
A Minimalist Home Menu for Nintendo Switch - December 2024
- ←→ Navigate
- QE Page (L / R)
- EnterZ Select (A)
- X Back (B)
Role
- Mobile Development
- UI + UX Design
Team
- Solo
Timeline
- Started: Dec 2024
- Expanded: May–Jun 2026
Overview
A minimalist home menu for Nintendo Switch
This Nintendo Switch App Launcher is a minimalist implementation of a traditional home menu. It relies on the JavaScript library nx.js, built by TooTallNate, to render React components to a generic canvas element through the react-tela library.
While the react-tela framework itself is runtime-agnostic, it was the only way I could find to run the JSX runtime on the Nintendo Switch. Even though this interface relies on minimal reactivity, I thought the concept of running a React app on a console was interesting enough to be worth exploring.
Low-Level Control The framework gave me shockingly low-level access to the hardware. Through the Canvas API, I was able to draw pixels and shapes directly to the screen instead of interacting with a Document Object Model (DOM), like in traditional JavaScript.
TypeScript Support Coming from a Java background, I'm a big fan of what TypeScript brings to JavaScript. Adding type safety to code is a common sense addition to any language, and it's become so ingrained in my workflow that I can't really live without it.
React Tooling React's declarative UI feels like a natural fit for a quick and dirty app like this. Reacting to state changes felt far more natural to work with than manually updating the UI, especially when it came to handling controller input.
(as of June 2026)
Context
The React I remember, but without strings attached
In the Winter of 2024, I found myself with a full break from school for the holidays and a lot of time to kill. It made sense to start working on something new that I could show off, while also keeping the scope small enough that I could realistically finish the thing by the end of the break.
I had already been pretty invested in the world of Nintendo homebrew by this point. I had custom firmware running on not just my switch, but pretty much every Nintendo console I owned (Wii, DSI, 3DS, and Wii U). So I really liked the idea of taking a stab at building an app for a Nintendo console. However, I found the typical options to be a little lacking. Working with C/C++ seemed like it would be too much of a pain to get started, and I didn't want to spend too much time tinkering with the low-level details of the console's APIs in order to build a simple app.
While JavaScript may not be an obvious choice, it's actually relatively fast compared to other high level options like Python, and its frontend ecosystem plays well with UI development. Ultimately, nx.js ended up being amazing for those reasons.
Components like Rect, Text, and Image from react-tela act as the building blocks of the UI.
The Custom React renderer walks through the elements and queues them to be painted to the screen with the Canvas2D API.
The render function paints straight onto the Switch's screen.
Iterating through the Switch's app catalog
The launcher reads the Switch's app catalog and filters out any apps that don't have an icon pre-defined, as those applications are likely corrupt or missing.
const switchApps = Array.from(Switch.Application).filter(
(app) => app.icon
);Decoding individual box art for the canvas
The launcher then decodes the box art for each app and stores it in a bitmap for the Canvas API to render onto the screen.
for (const app of switchApps) {
let img;
if (app.icon) {
img = await createImageBitmap(new Blob([app.icon]));
}
if (!img) continue;
//...
}Project timeline
Four bursts across 92 commits
This timeline groups the four biggest sprints of the project with the features that were shipped in each.
-
First build
A launcher over the holiday break
What I built. The initial base of the project that pulls from the Switch's app catalog to draw icons to the screen and launch apps via A-press. It displays a row of apps in pages, using the L and R shoulder buttons to skip between pages.
skeleton nxjs baseapp launching functionalityshoulder buttons paginationselections via dpadautomate font bundlingWhy it mattered Controller-based browsing and app launchingWithout the basic rendering loop and app launching functionality, there would be no project to build upon.
-
Off-console iteration
Move iteration off the console
What I built. This sprint focused on implementing the Vite pipeline, allowing for the React component tree to be fully device-agnostic and render in the browser. It also added a set of comprehensive settings to mutate the UI's appearance and behavior.
implement vite pipeline with browser previewimplement pagination dotsimplement last played trackingimplement compact viewimplement custom sort modeWhy it mattered Testing without Switch hardwareTesting in the browser proved to be a huge time saving measure, as I could patch the UI in real-time without having to build and deploy to the console every time I wanted to test a change.
-
Growing the surface
Turn a launcher into a navigable home surface
What I built. This turned the launcher from a basic interface with Switch home menu feature parity into a fully-featured home surface with search, tags, and a photo album. This iteration used a local database compiled at build metadata for each game to display metadata for each game in a rich detail view.
implement showing page #s featureadd album pageimplement web appletimplement cached loads with manual invalidationimplement game tagging with badge displayadd search bar functionalityimplement tag autocomplete in searchWhy it mattered Providing context and aesthetic appealThis iteration allowed the project to differentiate itself from the original menu and give it functionality more akin to a PlayStation home menu. The gradients and colors were a nice touch, and the rich detail panel was a nice touch to add some visual flair to the project without adding too much technical overhead.
-
Cleanup
Stabilize on console hardware
What I built. This sprint focused on cleaning up documentation, squashing bugs, and migrating the static database to dynamic RAWG fetching. By this time, I had worked out how to use fetch and a simple proxy to get game metadata without having to deal with a pre-generated database.
Expand README.mdreplace mockup img referenced in READMEprune legacy igdb database (commit reports 64% smaller bundle)fix virtual-keyboard lifecycle crash after RAWG loadingWhy it mattered 15.2 MB catalog removed; measured romfs ≈91% smallerMigrating the database to dynamic fetching massively reduces the bundle size and allows the project to include future titles that may not have been available at the time of the initial build.
Development
One interface pipeline, two execution environments
The development process was a bit of a rollercoaster. I started out by trying to use the Canvas API directly, but it was a bit of a pain to work with. I ended up using the react-tela library to render the React components to a canvas, which was a lot easier to work with.
The same React tree that runs on Switch is available in the interactive browser preview at the top of this page. Game metadata is fetched through a server-side RAWG proxy, so the API key never ships to the browser.
The React framework was the foundation of this project. It allowed for me to declare how I wanted the UI to look, without having much regard for what platform it was built for. This platform agnostic approach allowed me to focus on the core features I wanted to build, without worrying about the nitty gritty details.
src/main.tsx This version of React allows the project to adapt its UI to paint to the Switch's screen through the Canvas2D API, taking full advantage of the Switch’s rendering pipeline and performance characteristics.
src/browser/main.tsx The browser uses polyfilling to emulate Switch-like functionality, mapping gamepad inputs to the keyboard and mouse inputs to touch. The browser view renders in a scaled 1280 x 720 window to match the Switch's aspect ratio.
Catalog and layout I also had to become familiar with the Switch.Application API. It reads the switch's app titles, authors, and box art from the Switch's app catalog. Each image renders exactly 40 pixels away from each other so that the grid can fit exactly 4 apps across the Switch's 1280 pixel width screen.
Input and selection chrome Controller inputs are handled by a custom hook that listens for button presses and updates the UI accordingly. It listens for both left and right directional inputs on the stick and D-pad, as well as detecting shoulder buttons to quickly swap pages. It also listens for the A button to launch whatever app is selected.
Final Build
What the project supports today
The project leads with a modular presentation, giving the user options to switch between different views. Users can display their games in a row, a grid, or a list. They can also search for games by title, genre, or tag. While the rich details panel is the highlighted feature, it can be freely disabled if the user prefers a more minimal approach.
Grid + compact list
Controller, touch, and shoulder-button navigation
Search + tags
Full-text filtering, tag chips, and autocomplete
Metadata cache + editing
Offline resume, refresh, and per-title overrides
Settings + sorting
Recent, alphabetical, release, launch count, and custom order
Photo album
Grid browsing, lightbox navigation, and delete confirmation
Web shortcut
System browser handoff through the Switch's native web browser shortcut
Browser preview
Vite, nx.js port, keyboard gamepad, and mouse touch
The rich-details view allows users to see more information about the given game they're playing, courtesy of RAWG.
Keyboard as gamepad
Rehearse console navigation in the browser preview. By mapping keyboard keys to gamepad controls in the browser, developers can simulate authentic console navigation without having to touch bare-metal hardware.
Searching and sorting
Full-text filtering, tag chips, and autocomplete, with a custom sort mode in the settings. The search bar allows users to filter down games based on key characteristics like title, genre, and tag.
Progressive game detail
Access game metadata right from the menu itself. The rich detail panel gives the menu some color, while giving users more information about the game(s) they're playing.