How to Contribute to Invidious Frontend Development: A Complete Guide
To contribute to Invidious frontend development, modify the server-rendered ECR templates in src/invidious/views/ for structural changes, edit the client-side JavaScript in assets/js/ for interactive features, and rebuild static assets using make assets before submitting your pull request to the iv-org/invidious repository.
Invidious uses a hybrid architecture combining Crystal-based server-side rendering with client-side JavaScript to deliver a lightweight YouTube frontend. Understanding how data flows from Crystal route handlers through ECR templates into JSON blobs consumed by Video.js-powered scripts is essential for making effective contributions.
Understanding the Frontend Architecture
The Invidious frontend consists of two distinct layers that communicate through embedded JSON data structures.
Server-Side Rendering with ECR Templates
Each page is assembled from ECR (Embedded Crystal) templates that receive strongly typed Crystal models such as VideoData and PlayerData. These views inject JSON blobs directly into the DOM using script tags like <script id="player_data">…</script>, which client-side code then consumes. Key templates include src/invidious/views/watch.ecr for the video page and component partials like src/invidious/views/components/player.ecr and src/invidious/views/components/subscribe_widget.ecr.
Client-Side JavaScript Stack
The assets/js/ directory contains the bundled JavaScript that powers interactivity. The player.js file initializes Video.js, attaches hotkey listeners, handles caption loading, and manages the control bar through the controlBar.children configuration array. The watch.js file acts as UI glue, managing playlist navigation, comment source switching via swap_comments, and continue-autoplay logic through the next_video function. Helper utilities reside in assets/js/_helpers.js, providing helpers.xhr for API calls and helpers.storage for local state.
Data Flow from Routes to Renderer
When a user requests a video, the flow follows this pattern:
- Route handler –
src/invidious/routes/watch.crcontainsInvidious::Routes::Watch.handle, which parses query parameters, fetches metadata, and constructs thevideo_dataandplayer_datastructs (lines 86–92 for asset construction). - Template rendering –
watch.ecrrenders the HTML and embeds the data as JSON text content inside script tags. - Client initialization –
assets/js/player.jsparses these blobs usingJSON.parse(document.getElementById('player_data').textContent)and initializes the Video.js instance with quality selectors and hotkey bindings. - State persistence – User interactions trigger updates to the
PREFScookie via theupdateCookiehelper (lines 1088–1115 inplayer.js).
Setting Up Your Development Environment
Before making frontend contributions, prepare a local instance with Crystal and Node tooling.
- Install Crystal version 1.12 or higher.
- Clone the repository and run
make devto start the development server onhttp://localhost:3000. - Run
make assetsto compile and minify JavaScript and CSS into the final static bundle. - Execute
make testto run Crystal unit tests and the JavaScript linter vianpm run lint.
Common Frontend Contribution Patterns
Most frontend tasks involve extending existing patterns in either the ECR templates or the JavaScript modules.
Adding Player Controls
To add a button to the Video.js control bar, modify assets/js/player.js by extending the options.controlBar.children array (defined around lines 12–28) and registering a custom component. For example, adding a loop toggle requires inserting the component name before the fullscreenToggle entry and defining the click handler that calls player().loop().
Modifying Comment Sections
Comment source switching lives in assets/js/watch.js within the swap_comments function (lines 14–22). To add a new comment provider, extend the conditional logic to handle your new source type, then update the ECR template at src/invidious/views/components/comments.ecr to include the new toggle link with the appropriate data-comments attribute.
Persisting User Preferences
Runtime preferences such as volume and playback speed use the updateCookie helper in assets/js/player.js. When adding a new preference like dark mode, toggle the class on the document body, then write to a dedicated cookie using the same formatting pattern used for PREFS in lines 1088–1115.
Step-by-Step Contribution Workflow
Follow this sequence when implementing frontend features:
- Identify the target layer – Determine if your change requires modifying a Crystal route (
src/invidious/routes/*.cr), an ECR view (src/invidious/views/*.ecr), or client-side scripts (assets/js/*.js). - Follow existing patterns – For JavaScript, use the module style in
player.js: declare helper functions, usehelpers.xhrfor API calls, and attach listeners viaplayer.on. For ECR, use Crystal interpolation ({{ }}) and avoid inline scripts; place data in JSON blobs instead. - Implement and rebuild – After editing JavaScript or CSS, run
make assetsto regenerate the static bundle. - Validate locally – Open the affected page in your browser, check the console for errors, and verify that hotkeys (handled in lines 61–78 of
player.js), playback rate controls, and caption toggles remain functional. - Run quality checks – Execute
make testto ensure Crystal tests pass and JavaScript linting succeeds. - Submit your changes – Fork the repository, create a feature branch, commit with clear messages, and open a pull request following the guidelines in the project README.
Key Source Files for Frontend Developers
When contributing to Invidious frontend development, these files contain the core logic:
src/invidious/routes/watch.cr– Handles HTTP requests for video pages, constructsVideoAssetsstructs, and performs permission checks before rendering.src/invidious/views/watch.ecr– Main template for video pages that injects JSON data and includes component partials.src/invidious/views/components/player.ecr– Renders the HTML5 video element markup consumed by Video.js.assets/js/player.js– Core client logic including Video.js initialization, hotkey handling (lines 61–78), and cookie management.assets/js/watch.js– Playlist navigation, autoplay logic (lines 30–43 fornext_video), and comment source switching.assets/js/_helpers.js– Utility functions for XHR requests and storage management used across the frontend.Makefile– Definesdev,assets, andtesttargets for building and validation.
Summary
- Invidious frontend development requires understanding both Crystal/ECR server-side rendering and Video.js client-side initialization.
- Modify
src/invidious/routes/watch.crto change data structures andassets/js/player.jsto adjust player behavior. - Use
make assetsto rebuild static files andmake testto verify changes before submitting. - Follow the established data flow: route handlers populate structs, ECR templates embed JSON, and JavaScript parses the data to initialize UI components.
- Persist user preferences using the
updateCookiehelper and thePREFScookie pattern.
Frequently Asked Questions
What programming languages do I need to know to contribute to Invidious frontend development?
You need Crystal for server-side route handlers and ECR templates, plus JavaScript for client-side interactivity. Understanding Video.js APIs is essential for player modifications, while basic SQL knowledge helps if your changes involve the database layer that feeds the frontend models.
How do I test my frontend changes locally?
Run make dev to start a local server on port 3000 after installing Crystal 1.12 or higher. Modify your target files, then run make assets to compile JavaScript and CSS. Refresh your browser to see changes, and use make test to run the Crystal unit test suite and JavaScript linter before committing.
Where is the video player logic implemented?
The core player initialization resides in assets/js/player.js, which parses the JSON data injected by watch.ecr and configures Video.js instances. Hotkeys are handled in lines 61–78, while the options.controlBar.children array (lines 12–28) defines which controls appear in the player interface.
How are user preferences like volume and playback speed stored?
Preferences persist through a PREFS cookie that the server reads on subsequent requests. The updateCookie function in assets/js/player.js (lines 1088–1115) handles writing these values from JavaScript when users adjust settings, ensuring state survives page reloads.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →