Skip to main content

How we develop frontend components

This is how we build components for the Owncast web UI. Use it when changing existing components or adding new ones. A shared pattern keeps the project readable and maintainable.

Functional componentsโ€‹

React has two ways to write components: class-based and functional. Class-based has fallen out of favor, so we write functional components. See the React component docs.

The pattern we useโ€‹

Statelessโ€‹

export type MyNewButtonProps = {
label: string;
onClick: () => void;
};

export const MyNewButton: FC<MyNewButtonProps> = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);

Statefulโ€‹

export type MyNewButtonProps = {
label: string;
onClick: () => void;
};

export const MyNewButton: FC<MyNewButtonProps> = ({ label, onClick }) => {
const handleClick = useCallback(() => {
alert(label);
onClick && onClick();
}, [label, onClick]);

return <button onClick={handleClick}>{label}</button>;
};

There are many common ways to write components, so settling on one keeps things consistent. For why this style, see the PR that introduced it.

Error boundariesโ€‹

Components with substantial state and internal logic should be wrapped in an error boundary, so an unexpected error shows a fallback instead of crashing the page. Stateless view components rarely throw and don't need one. The ComponentError component is a prebuilt error state with a bug-reporting button.

import { ErrorBoundary } from 'react-error-boundary';

<ErrorBoundary
fallbackRender={({ error, resetErrorBoundary }) => (
<ComponentError
componentName="DesktopContent"
message={error.message}
retryFunction={resetErrorBoundary}
/>
)}
>
<YourComponent />
</ErrorBoundary>

Storybookโ€‹

We use Storybook as a component library where you can see and interact with each component. Include a .stories.tsx file with every exported component, and update it when you change a component. Run the Storybook server with npm run storybook.

Linting and formattingโ€‹

We use Prettier and ESLint for JavaScript and TypeScript. Set them up in your editor or run them manually. Linting or formatting errors will block a PR until they are fixed.