How Are Components Managed in the Stremio-Web Codebase: A Complete Guide
Components in stremio-web are organized as self-contained folders under src/components/, centrally re-exported through src/components/index.ts, and consumed via the stremio/components module alias to ensure tree-shaking and maintainable scaling.
The Stremio web application (Stremio/stremio-web) is built with React and TypeScript, employing a modular architecture that keeps UI elements discoverable and encapsulated. This article examines the specific patterns used to manage components, from file organization to import resolution.
Component Architecture Overview
Every reusable UI element lives under src/components/ as a self-contained folder. Each folder typically contains:
- A TypeScript implementation file (
*.tsx) - A Less stylesheet (
*.less) - An optional test suite
This structure ensures that logic, styling, and tests are co-located, making individual components easy to locate and move. The architecture deliberately separates UI concerns from business logic, which resides in src/services/.
Component Anatomy and File Structure
Self-Contained Component Folders
Components follow a strict co-location pattern. For example, the Button component exists at src/components/Button/ and contains both Button.tsx and Button.less. This guarantees that any file importing Button automatically brings along its scoped styles.
Implementation Patterns
Components are implemented as pure functional components using React hooks. They utilize forwardRef for ref forwarding and import styles as CSS Modules.
Here is the implementation pattern from src/components/Button/Button.tsx:
import { createElement, forwardRef, useCallback } from 'react';
import classNames from 'classnames';
import { LongPressEventType, useLongPress } from 'use-long-press';
import styles from './Button.less';
type Props = {
className?: string;
href?: string;
disabled?: boolean;
children: React.ReactNode;
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
onLongPress?: () => void;
};
const Button = forwardRef(({ className, href, disabled, children, onLongPress, ...props }: Props, ref) => {
const longPress = useLongPress(onLongPress!, { detect: LongPressEventType.Pointer });
return createElement(
typeof href === 'string' && href.length > 0 ? 'a' : 'div',
{
tabIndex: 0,
...props,
ref,
className: classNames(className, styles['button-container'], { disabled }),
href,
...longPress(),
},
children
);
});
export default Button;
Key implementation details include:
- Logic – Managed through hooks like
useCallbackand custom hooks such asuseLongPress. - Styling – Scoped via CSS Modules (
Button.less), referenced asstyles['button-container']. - Export – Default export for individual consumption, later aggregated in the central index.
Centralized Component Registry
The Index Hub Pattern
The file src/components/index.ts serves as the single entry point for the entire component library. It imports each component from its respective folder and re-exports them as named exports:
// src/components/index.ts
import Button from './Button';
import Toggle from './Toggle';
import Image from './Image';
// ... other imports
export {
Button,
Toggle,
Image,
// ... all other components
};
This pattern enables developers to import multiple components from a single path while maintaining tree-shaking compatibility.
Module Aliasing with Webpack and TypeScript
To avoid relative path hell (e.g., ../../../components/Button), the project configures a module alias stremio/components that resolves to src/components/.
Webpack configuration maps the alias for bundling, while TypeScript path mapping in tsconfig.json provides IDE support and compile-time checking:
{
"paths": {
"stremio/components/*": ["src/components/*"]
}
}
This allows concise, predictable imports throughout the application:
import { Button, Toggle, Image } from 'stremio/components';
Import and Usage Patterns
The alias is used extensively across the application. For example, src/routes/Settings/Menu/Menu.tsx imports the Button component:
// src/routes/Settings/Menu/Menu.tsx
import { Button } from 'stremio/components';
// Usage within component
<Button onClick={handleClick}>Save</Button>
Higher-level compositions, such as src/App/UpdaterBanner/UpdaterBanner.tsx, demonstrate how multiple components are combined:
import { Button, Image, Transition } from 'stremio/components';
export const UpdaterBanner = () => (
<Transition when={true} name="fade" duration={300}>
<Button onClick={() => console.log('clicked')}>Update Now</Button>
<Image src="/logo.png" alt="Stremio logo" />
</Transition>
);
Styling Strategy with CSS Modules
Components import adjacent .less files that are processed through the CSS Modules loader. This scopes class names locally, preventing global namespace pollution.
In src/components/Button/Button.less, classes like .button-container are transformed at build time. The component references these via the imported styles object:
className={classNames(className, styles['button-container'], { disabled })}
This approach ensures that styles defined for a button do not leak to other elements, even if class names overlap.
State Management and Lifecycle
Most components in stremio-web are stateless pure UI widgets. When state is required (for example, in toggle switches), components manage it internally via useState or through shared React Contexts such as GamepadContext and CoreProvider.
Business logic is deliberately kept out of components, residing instead in services under src/services/. This separation of concerns keeps components focused on presentation while state and side effects are handled by dedicated providers.
Adding a New Component
To extend the component library, follow this workflow:
- Create the folder at
src/components/MyNewComponent/. - Add the implementation in
MyNewComponent.tsxfollowing the hooks andforwardRefpattern. - Add styles in
MyNewComponent.less. - Register the export by adding
import MyNewComponent from './MyNewComponent';tosrc/components/index.tsand including it in the export list. - Consume the component via
import { MyNewComponent } from 'stremio/components';.
Summary
- Components reside in self-contained folders under
src/components/, each containing implementation, styles, and optional tests. src/components/index.tsacts as a central registry, re-exporting all components for consumption.- The
stremio/componentsmodule alias (configured in Webpack and TypeScript) enables clean, absolute imports. - CSS Modules provide scoped styling via
.lessfiles imported as objects. - Components are predominantly stateless; state is handled via React Contexts or local hooks, with business logic living in
src/services/.
Frequently Asked Questions
Where are UI components located in the stremio-web codebase?
All UI components are located in the src/components/ directory. Each component occupies its own subfolder (e.g., src/components/Button/) containing the TypeScript implementation file, a Less stylesheet, and optional test files.
How does the stremio/components import alias work?
The alias is defined in both the Webpack configuration and tsconfig.json. It maps the string stremio/components to the physical path src/components/, allowing developers to import components using absolute paths rather than relative ones. This provides IDE autocomplete support and ensures consistent import syntax across the application.
What styling approach does stremio-web use for components?
The codebase uses CSS Modules with Less preprocessing. Each component imports a .less file, and the CSS Modules loader scopes all class names to that specific component. This prevents style leakage and allows developers to use simple class names like .button-container without worrying about global conflicts.
How do I add a new component to the stremio-web codebase?
Create a new folder under src/components/ with your .tsx and .less files, then register the component in src/components/index.ts by importing it and adding it to the export list. Once registered, you can import it anywhere in the application using import { MyComponent } from 'stremio/components';.
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 →