This file provides guidance to Claude Code when working with the MLflow frontend code in this directory.
IMPORTANT: Always be consistent with the rest of the repository. This is extremely important!
Before implementing any feature:
- Read through similar files to understand their structure and patterns
- Do NOT invent new components if they already exist
- Use existing patterns and conventions found in the codebase
- Check for similar functionality that already exists
IMPORTANT: Always start the development server from the repository root for the best development experience with hot reload:
# MUST be run from the repository root
nohup uv run bash dev/run-dev-server.sh > /tmp/mlflow-dev-server.log 2>&1 &
# Monitor the logs
tail -f /tmp/mlflow-dev-server.log
# Servers will be available at:
# - MLflow backend: http://localhost:5000
# - React frontend: http://localhost:3000 (with hot reload)This provides fast edit-refresh for UI development - changes to React components will automatically reload in the browser.
When running from the repository root, use this pattern:
# Example: Run any yarn command from root
pushd mlflow/server/js && yarn <command>; popdAvailable scripts:
# Development
yarn start # Start dev server (port 3000) with hot reload
yarn build # Build production bundle
# Testing
yarn test # Run Jest tests
yarn test:watch # Run tests in watch mode
yarn test:ci # Run tests with coverage for CI
# Code Quality
yarn lint # Run ESLint
yarn lint:fix # Run ESLint with auto-fix
yarn prettier:check # Check Prettier formatting
yarn prettier:fix # Fix Prettier formatting
yarn type-check # Run TypeScript type checking
# Other Commands
yarn storybook # Start Storybook for component development
yarn build-storybook # Build static Storybook
yarn i18n:check # Check i18n translationsIMPORTANT: Always run these checks and fix any remaining issues before committing:
# From repository root
pushd mlflow/server/js
yarn lint
yarn prettier:check
yarn i18n:check
yarn type-check
popd
# Fix any issues that are reportedAlways use components from @databricks/design-system when available. Do not create custom components if they already exist in the design system.
Common components include:
Button,IconButton- for actionsInput,Textarea,Select- for form inputsModal,Drawer- for overlaysTable,TableRow,TableCell- for data tablesTabs,TabPane- for tabbed interfacesAlert,Notification- for feedbackSpinner,Skeleton- for loading statesTooltip,Popover- for additional informationCard- for content containersTypography- for text styling
Example import:
import { Button, Modal, Input } from '@databricks/design-system';Prefer Button from @databricks/design-system over raw HTML <button> elements for most interactive actions:
// ✅ GOOD - Use DuBois Button for actions
<Button componentId="my-feature.action" type="tertiary" onClick={handleClick}>
Click me
</Button>
// ❌ BAD - Avoid raw HTML button for typical actions
<button type="button" onClick={handleClick}>
Click me
</button>Use the design system theme for consistent styling:
import { useDesignSystemTheme } from '@databricks/design-system';
const Component = () => {
const { theme } = useDesignSystemTheme();
return (
<div
style={{
color: theme.colors.textPrimary,
padding: theme.spacing.md,
fontSize: theme.typography.fontSizeBase,
}}
>
Content
</div>
);
};ALWAYS use theme.spacing values instead of hard-coded pixel widths. This ensures consistency and maintainability across the application.
// ✅ GOOD - Use theme spacing
<div style={{
padding: theme.spacing.md,
marginBottom: theme.spacing.lg,
gap: theme.spacing.sm
}} />
// ❌ BAD - Avoid hard-coded pixels
<div style={{
padding: '16px',
marginBottom: '24px',
gap: '8px'
}} />Common spacing values:
theme.spacing.xs- Extra small spacing (4px)theme.spacing.sm- Small spacing (8px)theme.spacing.md- Medium spacing (16px)theme.spacing.lg- Large spacing (24px)theme.spacing.xl- Extra large spacing (32px)
For custom spacing needs, use the spacing function:
// When you need a specific multiple of the base unit
padding: theme.spacing(2.5); // 20px (2.5 * 8px base unit)When looking for a component:
- First check
@databricks/design-systemimports in existing code - Component names may not be exact (e.g., "dropdown" could be
Select,DialogCombobox, orDropdownMenu) - Look at similar UI patterns in the codebase for examples
- If multiple matches exist, choose based on the use case
To see ALL components available in the design system:
# From mlflow/server/js directory, check what's exported
cat node_modules/@databricks/design-system/dist/design-system/index.d.ts
# This file lists every component as: export * from './ComponentName';
# Each line represents a component you can importThis is the definitive source for available components - more reliable than checking folders since it shows only what's publicly exported.
You can use Playwright to view the component documentation and examples in Storybook:
https://ui-infra.dev.databricks.com/storybook/js/packages/du-bois/index.html?path=/docs/primitives-<component-name>--docs
For example:
- Alert:
https://ui-infra.dev.databricks.com/storybook/js/packages/du-bois/index.html?path=/docs/primitives-alert--docs - Button:
https://ui-infra.dev.databricks.com/storybook/js/packages/du-bois/index.html?path=/docs/primitives-button--docs - Modal:
https://ui-infra.dev.databricks.com/storybook/js/packages/du-bois/index.html?path=/docs/primitives-modal--docs
Use Playwright MCP to navigate to these URLs and see live examples, props documentation, and usage patterns.
For testing UI changes in a real browser, Claude Code can use the Playwright MCP (Model Context Protocol) integration.
To check if Playwright MCP is available:
- Look for browser testing tools in available MCP functions
- Try using browser navigation or screenshot capabilities
If Playwright MCP is not available and you need to test UI changes, you can install it:
claude mcp add playwright npx '@playwright/mcp@latest'Note: After installation, you must restart Claude Code for the integration to be available.
Once installed, you can:
- Navigate to the development server
- Take screenshots of UI components
- Interact with forms and buttons
- Verify UI changes are working correctly
Example workflow:
- Make changes to React components
- Wait for hot reload (automatic)
- Use Playwright to navigate to
http://localhost:3000 - Take screenshots or interact with the updated UI
- Verify the changes work as expected
mlflow/server/js/
├── src/
│ ├── experiment-tracking/ # Experiment tracking UI
│ ├── model-registry/ # Model registry UI
│ ├── common/ # Shared components
│ ├── shared/ # Shared utilities
│ └── app.tsx # Main app entry point
├── vendor/ # Third-party dependencies
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── webpack.config.js # Webpack bundler config
└── jest.config.js # Jest test configuration
- React 18: UI framework
- TypeScript: Type safety
- Redux: State management
- Apollo Client: GraphQL client
- Ant Design (antd): UI component library
- AG-Grid: Data table component
- Jest: Testing framework
- React Testing Library: Component testing
- Webpack: Module bundler
- Create component file in appropriate directory
- Add TypeScript types/interfaces
- Write component with hooks (functional components preferred)
- Add unit tests in same directory with
.test.tsxextension - Add to Storybook if it's a reusable component
- Modify query in relevant
.graphqlfile - Run codegen to update TypeScript types (if configured)
- Update components using the query
# Run tests for a specific component
yarn test ComponentName
# Run tests in watch mode for development
yarn test --watch
# Update snapshots if needed
yarn test -u- Use React Developer Tools browser extension
- Redux DevTools for state debugging
- Browser console for network requests
- Source maps are enabled in development mode
- Use functional components with hooks
- Prefer TypeScript strict mode
- Follow existing patterns in the codebase
- Use meaningful component and variable names
- Add JSDoc comments for complex logic
- Keep components small and focused
Use React Query for all API calls and data fetching:
// Good: Using React Query
const { data, isLoading, error } = useQuery({
queryKey: ['experiments', experimentId],
queryFn: () => fetchExperiment(experimentId),
});
// Avoid: Manual fetch in useEffect
// useEffect(() => { fetch(...) }, [])Avoid useEffect when possible. Prefer deriving state with useMemo:
// Good: Derive state with useMemo
const filteredRuns = useMemo(() => {
return runs.filter((run) => run.status === 'active');
}, [runs]);
// Avoid: useEffect to update state
// useEffect(() => {
// setFilteredRuns(runs.filter(run => run.status === 'active'));
// }, [runs]);Use useEffect only for:
- Side effects (DOM manipulation, subscriptions)
- Synchronizing with external systems
- Cleanup operations
- Use React.memo for expensive components
- Implement virtualization for large lists (AG-Grid handles this)
- Lazy load routes and heavy components