Invite-Only Early Access — Think Throo GitHub App is currently invite-only. Request access here.
2025November

Components structure in Umami codebase - Part 1.2

Inspired by BulletProof React, I applied its codebase architecture concepts to the Umami codebase.

This article focuses only on the components structure in Umami codebase.

Prerequisite

  1. Components structure in Umami codebase — Part 1.0

  2. Components structure in Umami codebase — Part 1.1

In Part 1.1, we reviewed the page.tsx and the WebsitesPage.tsx. In this article, we review:

  1. PageHeader, PageBody and Panel

  2. WebsitesDataTable.tsx

  3. WebsiteAddButton.tsx

PageHeader, PageBody and Panel

In the WebsitesPage.tsx, you will find the following import:

import { PageHeader } from '@/components/common/PageHeader';
import { Panel } from '@/components/common/Panel';
import { PageBody } from '@/components/common/PageBody';

You will find these components located at src/components/common

These “commonly” used components are abstracted and placed in components so they can be reused.

WebsitesDataTable.tsx

WebsitesDataTable.tsx is located in the same folder as page.tsx. and this component has the following definition:

import Link from 'next/link';
import { WebsitesTable } from './WebsitesTable';
import { DataGrid } from '@/components/common/DataGrid';
import { useLoginQuery, useNavigation, useUserWebsitesQuery } from '@/components/hooks';
 
export function WebsitesDataTable({
  userId,
  teamId,
  allowEdit = true,
  allowView = true,
  showActions = true,
}: {
  userId?: string;
  teamId?: string;
  allowEdit?: boolean;
  allowView?: boolean;
  showActions?: boolean;
}) {
  const { user } = useLoginQuery();
  const queryResult = useUserWebsitesQuery({ userId: userId || user?.id, teamId });
  const { renderUrl } = useNavigation();
 
  const renderLink = (row: any) => (
    <Link href={renderUrl(`/websites/${row.id}`, false)}>{row.name}</Link>
  );
 
  return (
    <DataGrid query={queryResult} allowSearch allowPaging>
      {({ data }) => (
        <WebsitesTable
          data={data}
          showActions={showActions}
          allowEdit={allowEdit}
          allowView={allowView}
          renderLink={renderLink}
        />
      )}
    </DataGrid>
  );
}

This is where the websites are fetched. Notice how the table rendering is dealt with in another component named WebsitesTable.

WebsiteAddButton.tsx

You will find the code in WebsiteAddButton.tsx:

import { useMessages, useModified } from '@/components/hooks';
import { useToast } from '@umami/react-zen';
import { Plus } from '@/components/icons';
import { WebsiteAddForm } from './WebsiteAddForm';
import { DialogButton } from '@/components/input/DialogButton';
 
export function WebsiteAddButton({ teamId, onSave }: { teamId: string; onSave?: () => void }) {
  const { formatMessage, labels, messages } = useMessages();
  const { toast } = useToast();
  const { touch } = useModified();
 
  const handleSave = async () => {
    toast(formatMessage(messages.saved));
    touch('websites');
    onSave?.();
  };
 
  return (
    <DialogButton
      icon={<Plus />}
      label={formatMessage(labels.addWebsite)}
      variant="primary"
      width="400px"
    >
      {({ close }) => <WebsiteAddForm teamId={teamId} onSave={handleSave} onClose={close} />}
    </DialogButton>
  );
}

We have some new findings here. 

  1. Plus is an icon imported from components/icons folder.

  2. DialogButton is imported from components/input/DialogButton 

Why not DialogButton be part of common folder? This is about colocating and since this is in input foler, we can expect other input related components located in this folder as shown in the below screenshot.

In the next article, we review the website detail page.

About me:

Hey, my name is Ramu Narasinga. Email: ramu.narasinga@gmail.com

Tired of AI-generated code that works but nobody understands? 

I spent 3+ years studying OSS codebases and wrote 350+ articles on what makes them production-grade. I built an open source tool that reviews your PR against your existing codebase patterns.

Your codebase. Your patterns. Enforced. 

Get started for free —thinkthroo.com

References:

  1. https://github.com/umami-software/umami/blob/master/src/app/(main)/websites/WebsiteAddButton.tsx

  2. https://github.com/umami-software/umami/blob/master/src/app/(main)/websites/WebsitesPage.tsx#L6

  3. https://github.com/umami-software/umami/blob/master/src/app/(main)/websites/WebsitesPage.tsx#L2C1-L2C57

  4. https://github.com/umami-software/umami/blob/master/src/app/(main)/websites/WebsitesTable.tsx

  5. https://github.com/umami-software/umami/blob/master/src/app/(main)/websites/WebsitesDataTable.tsx#L6