Guides safe modification of the TaxonomicFilter component — PostHog's multi-tab search/filter for selecting events, actions, properties, cohorts, and more. Covers the component hierarchy, kea logic architecture, RTL testing workflow, and common pitfalls learned from prior changes. Use when adding features, fixing bugs, or refactoring TaxonomicFilter or its sub-components.
The TaxonomicFilter is one of PostHog's most complex frontend components. It powers event/action/property/cohort selection across the entire app. Changes here have a high blast radius — always lock down behavior with tests before modifying.
Read the architecture — understand which logic and component you're touching. See references/architecture.md.
Write RTL tests first — capture the current behavior before making changes. See references/testing-patterns.md.
Check common pitfalls — avoid mistakes that have burned prior contributors. See references/common-pitfalls.md.
Run the existing test suite to establish a green baseline:
hogli test frontend/src/lib/components/TaxonomicFilter/
TaxonomicFilter <- entry point, creates logic props
├── TaxonomicFilterSearchInput <- keyboard events, search query
└── InfiniteSelectResults <- tab pills + per-tab lists
└── InfiniteList <- virtualized list per group
└── InfiniteListRow <- individual result row
Three kea logics coordinate behavior:
| Logic | Role | Key file |
|---|---|---|
taxonomicFilterLogic | Orchestrator — tabs, search, group ordering, top matches | taxonomicFilterLogic.tsx (~1600 lines) |
infiniteListLogic | Per-tab list — search, pagination, property promotion | infiniteListLogic.ts |
recentTaxonomicFiltersLogic | Recent selections persisted to localStorage | recentTaxonomicFiltersLogic.ts |
Each infiniteListLogic instance is keyed by listGroupType and connected to the parent taxonomicFilterLogic.
TaxonomicFilterGroupType is an enum with 40+ members (Events, Actions, PersonProperties, PageviewUrls, SuggestedFilters, etc.).
Each group type maps to a TaxonomicFilterGroup configuration object defining its endpoint, search behavior, display name, and value extraction.
Tabs are ordered by taxonomicGroupTypes prop order, but some groups get dynamically reordered.
"Shortcut" groups (PageviewUrls, Screens, EmailAddresses, etc.) are promoted after SuggestedFilters when present.
The groupAnalyticsTaxonomicGroupType selector normalizes group type names for analytics.
redistributeTopMatches() distributes search results across groups:
DEFAULT_SLOTS_PER_GROUP (5) slotsREDISTRIBUTION_PRIORITY_GROUPS (CustomEvents, PageviewUrls, Screens)MAX_TOP_MATCHES_PER_GROUP (10) results per groupWhile search results load, SKELETON_ROWS_PER_GROUP (3) placeholder rows appear.
Use isSkeletonItem() to distinguish skeletons from real results.
infiniteListLogic promotes properties matching exact search terms to the top of results.
PROMOTED_PROPERTIES_BY_SEARCH_TERM maps terms like 'url' to '$current_url' and 'email' to '$email'.
Always write tests before modifying behavior. Two levels of testing:
Test what users see and interact with. Located in TaxonomicFilter.test.tsx.
const rendered = renderFilter({ taxonomicGroupTypes: [TaxonomicFilterGroupType.Events] })
await waitFor(() => expect(screen.getByTestId('taxonomic-filter-searchfield')).toBeInTheDocument())
Key patterns: renderFilter() helper, expectActiveTab() helper, userEvent for keyboard/click simulation.
See references/testing-patterns.md for full details.
Test state transitions and async behavior. Located in taxonomicFilterLogic.test.ts.
const logic = taxonomicFilterLogic(logicProps)
logic.mount()
await expectLogic(logic, () => logic.actions.setSearchQuery('pageview')).toMatchValues({ searchQuery: 'pageview' })
Key patterns: expectLogic(), toMatchValues, toDispatchActions, manual mounting of dependent infiniteListLogic instances.
See references/testing-patterns.md for full details.
| File | Lines | Purpose |
|---|---|---|
frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx | ~240 | Entry component + search input |
frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx | ~1600 | Core orchestration logic |
frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts | ~500 | Per-tab list logic |
frontend/src/lib/components/TaxonomicFilter/types.ts | ~280 | All type definitions |
frontend/src/lib/components/TaxonomicFilter/InfiniteSelectResults.tsx | ~200 | Tab pills + list container |
frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx | ~300 | Virtualized list |
frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.test.tsx | ~660 | Component-level RTL tests |
frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts | ~710 | Logic-level tests |