Blog
Components structure in Umami codebase - Part 1.2

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. I study codebase architecture in large open-source projects.

Email: ramu.narasinga@gmail.com

I spent 200+ hours analyzing Supabase, shadcn/ui, LobeChat. Found the patterns that separate AI slop from production code. Stop refactoring AI slop. Start with proven patterns. Check out production-grade projects at 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