twentyhq on GitHub

twentyhq/twenty

Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.

30 files reviewed·August 1, 2026

View on GitHub

Top fix

Add a loading state to the send button during submission

See the fix

Verdict

Clean architecture, silent failures: the composer delegates well but says nothing when it matters most. Sending an email or hitting a missing account both produce dead air instead of signal, and that silence is the real risk.

Files Rams reviewed

packages/twenty-front/src/modules/side-panel/pages/compose-email/components/SidePanelComposeEmailPage.tsx

packages/twenty-front/src/modules/side-panel/pages/front-component/components/SidePanelFrontComponentPage.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/SidePanelChartFilterSubPage.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/SidePanelFieldRelationTableFieldsSubPage.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/SidePanelFieldsLayoutSubPage.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/SidePanelRecordTableFieldsSubPage.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/SidePanelRecordTableFilterSubPage.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/SidePanelRecordTableSortSubPage.tsx

packages/twenty-front/src/modules/side-panel/pages/record-page/components/SidePanelMergeRecordPage.tsx

packages/twenty-front/src/modules/side-panel/pages/record-page/components/SidePanelRecordPage.tsx

packages/twenty-front/src/modules/side-panel/pages/rich-text-page/components/SidePanelEditRichTextPage.tsx

packages/twenty-front/src/modules/side-panel/pages/search/components/SidePanelSearchRecordsPage.tsx

packages/twenty-front/src/modules/side-panel/pages/send-campaign-test/components/SidePanelSendCampaignTestPage.tsx

packages/twenty-front/src/pages/settings/applications/components/SettingsAppModalLayout.tsx

packages/twenty-front/src/modules/side-panel/pages/ai-chat-threads/components/SidePanelAiChatThreadsPage.tsx

packages/twenty-front/src/modules/side-panel/pages/ask-ai/components/SidePanelAskAiPage.tsx

packages/twenty-front/src/modules/app/components/App.tsx

packages/twenty-front/src/modules/app/components/DomainShell.tsx

packages/twenty-front/src/modules/app/components/I18nActivationGate.tsx

packages/twenty-front/src/modules/app/components/RootAppProviders.tsx

packages/twenty-front/src/modules/app/components/WorkspaceAppProviders.tsx

packages/twenty-front/src/modules/side-panel/pages/common/components/SidePanelSubPageNavigationHeader.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartFiltersDeletedFieldsWarning.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartFiltersSettings.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartFiltersSettingsInitializeStateEffect.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartLimitInfoBanner.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartSettings.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartTypeSelectionSection.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/NewFieldDefaultVisibilityToggle.tsx

packages/twenty-front/src/modules/side-panel/pages/page-layout/components/RegularTabSettingsContent.tsx

96/100

UX

2 serious
UXSerious

packages/twenty-front/src/modules/side-panel/pages/compose-email/components/SidePanelComposeEmailPage.tsx:95

Send button gives no feedback while the email is sending

The "Send" button (key="send") calls composerState.handleSend on click and is disabled only via composerState.canSend, which reflects form validity, not send-in-progress state. There is no loading, spinner, or disabled-during-send state visible in the render. A user can click "Send" multiple times, or trigger the ctrl/meta+Enter hotkey again, while the async send is still in flight.

Why it matters

Without a busy state, duplicate clicks during network latency can trigger duplicate sends, and users get no confirmation their click registered, causing repeat clicks and possible duplicate emails.

Fix

Disable the Send button and show a loading indicator whenever a send is in progress, driven by an isSending flag from composerState.

<Button
  key="send"
  size="small"
  variant="primary"
  accent="blue"
  title={t`Send`}
  Icon={IconSend}
  hotkeys={[getOsControlSymbol(), '⏎']}
  onClick={composerState.handleSend}
  disabled={!composerState.canSend}
/>,
<Button
  key="send"
  size="small"
  variant="primary"
  accent="blue"
  title={composerState.isSending ? t`Sending...` : t`Send`}
  Icon={IconSend}
  hotkeys={[getOsControlSymbol(), '⏎']}
  onClick={composerState.handleSend}
  disabled={!composerState.canSend || composerState.isSending}
/>,
UXSerious

packages/twenty-front/src/modules/side-panel/pages/compose-email/components/SidePanelComposeEmailPage.tsx:74

Missing connected account silently renders an empty panel

When composeEmailConnectedAccountId is falsy, the component returns null at line 74-76 with no fallback UI. The side panel container this mounts in still opens, so the user sees an empty pane with no error text, no explanation, and no way to reconnect an account.

Why it matters

A user who tries to compose an email with a disconnected or missing account gets a blank panel instead of an explanation, so they don't know whether the app is broken or what action fixes it.

Fix

Render an explicit empty/error state with a message and a recovery action instead of returning null when the connected account is missing.

if (!composeEmailConnectedAccountId) {
  return null;
}
if (!composeEmailConnectedAccountId) {
  return (
    <StyledContainer>
      <StyledContent>
        <ErrorState
          title={t`No connected account`}
          description={t`Connect an email account to compose a message.`}
        />
      </StyledContent>
    </StyledContainer>
  );
}

Accessibility

No issues found

Typography

No issues found

Color

No issues found

Spacing

No issues found

Components

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • The send hotkey (ctrl/meta+Enter) is wired through useHotkeysOnFocusedElement and mirrors the hotkeys={[getOsControlSymbol(), '⏎']} label shown right on the "Send" button, so the shortcut is discoverable instead of hidden.
  • "Cancel" is rendered as variant="secondary" while "Send" is variant="primary" accent="blue", giving a clear filled-vs-outline hierarchy between the two footer actions rather than two competing buttons.

Scored August 1, 2026 with Rams Engine v0.0.3 · Engine changelog
First scored July 9, 2026: 98/100. This rescore on v0.0.3: 96/100.

This page is an automated design review of twentyhq/twenty’s UI code: 30 files read against 309 versioned rules covering accessibility, color, typography, spacing, components, UX, motion, and craft. The score is out of 100; confirmed criticals cap it — one at 59, two at 49, three or more at 39.

More design scores

Score your own repo.

Free on public repos, no account. The same engine that scored this page reads your UI code and mints a score page like this one.

Public repos only. The full engine reviews the UI code and mints a public score page — we email you the link too. Already-scored repos open instantly.

Or get a design review on every pull requestInstall Rams