This file provides guidance to AI coding agents when working with code in this repository.
We're a startup. We move fast, iterate quickly, and embrace change. When implementing features:
- Favor pragmatic solutions over perfect architecture
- Code will likely change - don't over-engineer
- A/B experiments are common (GrowthBook integration)
- Test coverage isn't a goal - write tests that validate functionality, not hit metrics
This is a pnpm monorepo containing the daily.dev application suite:
| Package | Purpose |
|---|---|
packages/webapp |
Next.js web application (main daily.dev site) |
packages/extension |
Browser extension (Chrome/Opera) built with Webpack |
packages/shared |
Shared React components, hooks, utilities, and design system |
packages/storybook |
Component documentation and development environment |
packages/eslint-config |
Shared ESLint configuration |
packages/eslint-rules |
Custom ESLint rules including color consistency enforcement |
packages/prettier-config |
Shared Prettier configuration |
- Node.js v22.11 (see
package.jsonvoltaandpackageManagerproperties, also.nvmrc) - pnpm 9.14.4 for package management (see
package.jsonpackageManagerproperty) - TypeScript across all packages
- React 18.3.1 with Next.js 15 for webapp (Pages Router, NOT App Router/Server Components)
- TanStack Query v5 for server state and data fetching
- GraphQL with graphql-request for API communication
- Tailwind CSS with custom design system
- Jest for testing
- GrowthBook for feature flags and A/B experiments
// Buttons (variants: Primary, Secondary, Tertiary, Float, Subtle, Option, Quiz)
import { Button, ButtonVariant, ButtonSize } from '@dailydotdev/shared/src/components/buttons/Button';
import { ClickableText } from '@dailydotdev/shared/src/components/buttons/ClickableText';
// Typography
import { Typography, TypographyType, TypographyColor } from '@dailydotdev/shared/src/components/typography/Typography';
// Form Fields
import { TextField } from '@dailydotdev/shared/src/components/fields/TextField';
import { Switch } from '@dailydotdev/shared/src/components/fields/Switch';
import { Checkbox } from '@dailydotdev/shared/src/components/fields/Checkbox';
import { Radio } from '@dailydotdev/shared/src/components/fields/Radio';
// Layout & Utilities
import { FlexCol, FlexRow } from '@dailydotdev/shared/src/components/utilities';
import Link from '@dailydotdev/shared/src/components/utilities/Link';
// Icons (500+ available)
import { PlusIcon, ShareIcon, UpvoteIcon } from '@dailydotdev/shared/src/components/icons';
import { IconSize } from '@dailydotdev/shared/src/components/Icon';
// Modals
import { LazyModal } from '@dailydotdev/shared/src/components/modals/common/types';
// Feedback
import { Loader } from '@dailydotdev/shared/src/components/Loader';
import Toast from '@dailydotdev/shared/src/components/notifications/Toast';
import { Tooltip } from '@dailydotdev/shared/src/components/tooltip/Tooltip';// Most commonly used
import { useViewSize, ViewSize } from '@dailydotdev/shared/src/hooks';
import { useLazyModal } from '@dailydotdev/shared/src/hooks/useLazyModal';
import { useToastNotification } from '@dailydotdev/shared/src/hooks/useToastNotification';
import { useConditionalFeature } from '@dailydotdev/shared/src/hooks/useConditionalFeature';
import { useFeedLayout } from '@dailydotdev/shared/src/hooks/useFeedLayout';
import { usePrompt } from '@dailydotdev/shared/src/hooks/usePrompt';
// Actions & State
import { useActions, usePlusSubscription } from '@dailydotdev/shared/src/hooks';
import useFeedSettings from '@dailydotdev/shared/src/hooks/useFeedSettings';import { useAuthContext } from '@dailydotdev/shared/src/contexts/AuthContext';
import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext';
import { useSettingsContext } from '@dailydotdev/shared/src/contexts/SettingsContext';
import { useNotificationContext } from '@dailydotdev/shared/src/contexts/NotificationsContext';import type { Post, PostType } from '@dailydotdev/shared/src/graphql/posts';
import type { Source, SourceType } from '@dailydotdev/shared/src/graphql/sources';
import { gqlClient } from '@dailydotdev/shared/src/graphql/common';
import { ActionType } from '@dailydotdev/shared/src/graphql/actions';import type { LoggedUser } from '@dailydotdev/shared/src/lib/user';
import { LogEvent, Origin } from '@dailydotdev/shared/src/lib/log';
import { AuthTriggers } from '@dailydotdev/shared/src/lib/auth';
import { webappUrl } from '@dailydotdev/shared/src/lib/constants';
import classed from '@dailydotdev/shared/src/lib/classed';import { useForm, FormProvider } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
// Controlled components (use within FormProvider)
import ControlledTextField from '@dailydotdev/shared/src/components/fields/ControlledTextField';
import ControlledTextarea from '@dailydotdev/shared/src/components/fields/ControlledTextarea';
import ControlledSwitch from '@dailydotdev/shared/src/components/fields/ControlledSwitch';# Setup
nvm use # Use correct Node version from .nvmrc
npm i -g pnpm@9.14.4
pnpm install
# Development
pnpm --filter webapp dev # Run webapp (HTTPS)
pnpm --filter webapp dev:notls # Run webapp (HTTP)
pnpm --filter extension dev:chrome # Run Chrome extension
pnpm --filter storybook dev # Run Storybook
# Testing & Linting
pnpm --filter <package> test # Run tests
pnpm --filter <package> lint # Run linter
pnpm --filter <package> lint:fix # Fix lint issues
# Building for production
pnpm --filter webapp build # Build webapp
pnpm --filter extension build:chrome # Build Chrome extensionIMPORTANT: Do NOT run build commands while the dev server is running - it will break hot reload. Only run builds at the end to verify your work compiles successfully. During development, rely on the dev server's hot reload and TypeScript/ESLint checks instead.
Is it used by both webapp AND extension?
├── Yes → packages/shared/
│ ├── Is it a React component? → src/components/
│ ├── Is it a custom hook? → src/hooks/
│ ├── Is it a GraphQL query/mutation? → src/graphql/
│ ├── Is it a complex feature with multiple files? → src/features/
│ ├── Is it a React context? → src/contexts/
│ └── Is it a utility function? → src/lib/
├── No, webapp only → packages/webapp/
│ ├── Is it a page? → pages/
│ ├── Is it a layout? → components/layouts/
│ └── Is it webapp-specific logic? → components/ or hooks/
└── No, extension only → packages/extension/src/
├── Is it for new tab? → newtab/
├── Is it for companion widget? → companion/
└── Is it background logic? → background/
| Use Case | Solution |
|---|---|
| Server data (API responses) | TanStack Query |
| Global app state (user, settings) | React Context |
| Local/UI state | useState |
| Form state | react-hook-form + Zod validation |
Note: TanStack Query v5 uses isPending for mutations (not isLoading).
- Colors: Food-themed palette (burger, cheese, avocado, bacon, etc.)
- Use semantic tokens:
text-primary,bg-surface-primary, not raw colors - Typography: Use
typo-*classes (typo-title1, typo-body, typo-callout) - Responsive: mobileL, mobileXL, tablet, laptop, laptopL, desktop
- ESLint enforces
no-custom-colorrule - use design system tokens
We write tests to validate functionality, not to achieve coverage metrics:
- Focus on user interactions with React Testing Library
- Mock API responses with
nock - Test files live next to source:
Component.spec.tsx - Run tests:
pnpm --filter <package> test
GrowthBook is integrated for A/B testing:
import { useConditionalFeature } from '@dailydotdev/shared/src/hooks';
const { value, isLoading } = useConditionalFeature({
feature: 'feature_name',
shouldEvaluate: true,
});pnpm-workspace.yaml- Monorepo workspace packagespackages/webapp/next.config.ts- Next.js configurationpackages/shared/tailwind.config.ts- Base Tailwind configurationpackages/extension/webpack.config.js- Extension build configuration
Each package has its own AGENTS.md with detailed guidance:
packages/shared/AGENTS.md- Shared components, hooks, design systempackages/webapp/AGENTS.md- Next.js webapp specificspackages/extension/AGENTS.md- Browser extension developmentpackages/storybook/AGENTS.md- Component documentation
When adding new content (videos, images, text blocks) to existing pages:
- Study the page structure first - Identify existing sections and their purpose
- Integrate into existing components rather than creating new wrapper components
- Avoid duplicating section headers - If a page has "How it works", don't add "See how it works"
- Extend existing components - Add content to the relevant existing component instead of creating parallel components
Example: Adding a video to the jobs page
- ❌ Wrong: Create new
OpportunityVideocomponent with its own "See how it works" title - ✅ Right: Add the video embed inside the existing
OpportunityHowItWorkscomponent
- Extension uses
webextension-polyfillfor cross-browser compatibility - SVG imports are converted to React components via
@svgr/webpack - Tailwind utilities preferred over CSS-in-JS
- GraphQL schema changes require manual TypeScript type updates
- Avoid index/barrel exports - they easily cause dependency cycles; prefer direct file imports
Keep PR descriptions concise and to the point. Reviewers should not be exhausted by lengthy explanations.
When reviewing code (or writing code that will be reviewed):
- Delete dead code - Remove unused components, functions, exports, and files. Don't leave code "for later"
- Avoid confusing naming - Don't create multiple components with the same name in different locations (e.g., two
AboutMecomponents) - Remove unused exports - If a function/constant is only used internally, don't export it
- Clean up duplicates - If the same interface/type is defined in multiple places, consolidate to one location and import