Each company in Who To Bother has a dedicated page displaying all available contacts organized by category. Company pages are dynamically generated from JSON files in the data/companies/ directory.
Page Structure
Company pages follow the route pattern /$company where the company parameter matches the company’s unique ID.
Dynamic Route Configuration
The company page route is defined in src/app/$company.tsx with automatic data loading:
export const Route = createFileRoute ( "/$company" )({
loader : ({ params }) => {
const { company } = params ;
const companyDataMap = getCompanyDataMap ();
const companyData = companyDataMap [ company ];
if ( ! companyData ) {
throw new Error ( `Company " ${ company } " not found` );
}
return companyData ;
},
component: CompanyPage ,
});
Company data is loaded at build time using Vite’s import.meta.glob, which auto-discovers all JSON files in the companies directory.
Page Components
The page header displays:
Back navigation to homepage
Company name with logo
X (Twitter) logo indicating the platform
Company links for website, docs, GitHub, and Discord (if available)
// Company links are conditionally rendered (src/components/contacts-list.tsx:69-132)
< CompanyLinks
website = { company . website }
docs = { company . docs }
github = { company . github }
discord = { company . discord }
/>
Search and Filter
Each company page includes a local search feature to filter contacts:
Search Input
Type in the search box to filter contacts by product name, X handle, or email address
Real-Time Filtering
The contacts list updates immediately as you type (throttled to 300ms for performance)
Visual Highlighting
Matching products are highlighted in orange to make them easy to identify
Auto-Scroll
When using search, the page automatically scrolls to the first matching result
Contacts are organized into categories (e.g., “Product & Engineering”, “Support”, “Developer Relations”). Each category displays:
< h2 className = "uppercase tracking-wider text-xs" >
{ category . name }
</ h2 >
Each contact entry shows:
Product/Role Name The product or role (e.g., “Workers AI”, “API Support”). Click to copy all handles.
X Handles Up to 2 handles shown directly, with a “more” dropdown for additional contacts
Email Address Optional email contact link (if available)
Discord Link Optional Discord channel link (if available)
Handle Display Logic
The system handles multiple contacts intelligently (src/components/contacts-list.tsx:196-269):
2 or fewer handles:
{ contact . handles . map (( handle ) => (
< a href = { `https://x.com/ ${ handle . replace ( "@" , "" ) } ` } >
< Avatar />
< span > { handle } </ span >
</ a >
))}
More than 2 handles:
Shows first 2 handles inline
Displays “more” link
Opens popover with remaining handles on click
Clicking any product name copies all associated X handles to your clipboard, making it easy to mention multiple contacts in a single post.
Filter Implementation
The filtering system matches contacts against three fields (src/components/contacts-list.tsx:35-53):
function filterContactsByQuery (
categories : Category [],
searchQuery : string
) : Category [] {
const query = searchQuery . toLowerCase ();
return categories
. map (( category ) => ({
... category ,
contacts: category . contacts . filter (( contact ) => {
const productMatch = contact . product . toLowerCase (). includes ( query );
const handleMatch = contact . handles . some (( handle ) =>
handle . toLowerCase (). includes ( query )
);
const emailMatch = contact . email ?. toLowerCase (). includes ( query );
return productMatch || handleMatch || emailMatch ;
}),
}))
. filter (( category ) => category . contacts . length > 0 );
}
Categories with no matching contacts are automatically hidden from the filtered view.
Highlight Detection
Matching contacts are visually highlighted using the isContactHighlighted function:
function isContactHighlighted ( contact : Contact , searchQuery : string ) : boolean {
if ( ! searchQuery ) return false ;
const query = searchQuery . toLowerCase ();
return (
contact . product . toLowerCase (). includes ( query ) ||
contact . handles . some (( handle ) => handle . toLowerCase (). includes ( query )) ||
Boolean ( contact . email ?. toLowerCase (). includes ( query ))
);
}
URL Integration
Search Query Parameters
When you navigate to a company page from search results, the query is preserved in the URL:
/$company - Show all contacts
/$company?q=api - Filter to “api” matches
The query state is managed with nuqs:
const [ searchQuery , setSearchQuery ] = useQueryState (
"q" ,
parseAsString . withDefault ( "" ). withOptions ({
limitUrlUpdates: throttle ( 300 ),
})
);
URL query parameters make it easy to share links to specific products within a company page.
Copy Functionality
Copying Handles
Click any product name to copy all associated X handles to your clipboard:
const copyHandlesToClipboard = async (
product : string ,
handles : string []
) : Promise < void > => {
const handlesString = handles . join ( " " );
try {
await navigator . clipboard . writeText ( handlesString );
setCopiedProduct ( product );
setTimeout (() => setCopiedProduct ( null ), 2000 );
} catch ( err ) {
console . error ( "Failed to copy:" , err );
}
};
Visual feedback:
Product name temporarily changes to “Copied!” in green
Resets after 2 seconds
Each company page includes dynamic metadata for better sharing:
head : ({ loaderData }) => {
const title = `who to bother at ${ loaderData . name } on X` ;
const description = `Find the right people to reach out to at ${ loaderData . name } on X (Twitter). ${ loaderData . description } ` ;
return {
meta: [ ... seo ({ title , description , ... })],
links: [
{
rel: "icon" ,
href: `/company-logos/ ${ loaderData . logoType } .svg` ,
}
],
};
}
Open Graph Images Each company page generates a unique Open Graph image at /og/[company-id] for rich social media previews.
Error Handling
If a company is not found, the page displays a friendly error:
errorComponent : ({ error }) => (
< div className = "flex min-h-screen items-center justify-center" >
< div className = "text-center" >
< h1 > Company Not Found </ h1 >
< p > { error . message } </ p >
< Link to = "/" > Back to home </ Link >
</ div >
</ div >
)
When filtering results, the page automatically scrolls to the first match:
useEffect (() => {
if ( searchQuery && firstMatchRef . current ) {
setTimeout (() => {
firstMatchRef . current ?. scrollIntoView ({
behavior: "smooth" ,
block: "start" ,
});
}, 100 );
}
}, [ searchQuery ]);
This ensures users immediately see relevant results without manual scrolling.