12 KiB
CLAUDE.md - MLflow Frontend Development
This file provides guidance to Claude Code when working with the MLflow frontend code in this directory.
Consistency is Critical
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
Development Server
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
uv run dev/run_dev_server.py > /tmp/mlflow-dev-server.log 2>&1 &
# Monitor the logs (server URLs are printed there)
tail -f /tmp/mlflow-dev-server.log
This provides fast edit-refresh for UI development - changes to React components will automatically reload in the browser.
Available Yarn Scripts
When running from the repository root, use this pattern:
# Example: Run any yarn command from root
pushd mlflow/server/js && yarn <command>; popd
Available 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 translations
Before Committing
IMPORTANT: 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 reported
UI Components and Design System
Use Databricks Design System Components
Always 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';
Avoid HTML Elements When Design System Alternatives Exist
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>
Theme Usage
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>
);
};
Empty States
IMPORTANT: Empty states must always be centered both vertically and horizontally within their container.
When implementing empty states using the Empty component from @databricks/design-system, always wrap it in a centered container with styles to override the Design System's internal layout:
import { Empty, SearchIcon } from '@databricks/design-system';
// ✅ GOOD - Empty state centered in container with Design System style overrides
const emptyComponent = (
<div
css={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%', // Take full height of parent
minHeight: 400, // Ensure minimum height for visibility
width: '100%',
// Override Design System's Empty component internal styles
'& > div': {
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
}}
>
<Empty
description="No items found. Try clearing your filters."
image={<SearchIcon />}
/>
</div>
);
// ❌ BAD - Empty state not centered
const emptyComponent = <Empty description="No items found" image={<SearchIcon />} />;
Key points for empty states:
- Use
display: 'flex'withalignItems: 'center'andjustifyContent: 'center' - Set
height: '100%'to fill the parent container's height - Set
minHeight(typically 400px or more) to ensure vertical centering is visible - Always set
width: '100%'to ensure proper horizontal centering - CRITICAL: Override the Design System's
Emptycomponent internal wrapper styles using'& > div'selector to ensure it takes full height and centers content - Use meaningful icons and descriptions to guide users
Spacing Guidelines
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)
Finding the Right Component
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
Discovering Available Components Dynamically
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 import
This is the definitive source for available components - more reliable than checking folders since it shows only what's publicly exported.
Viewing Component Documentation in Storybook
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.
Browser Testing with Playwright
For testing UI changes in a real browser, Claude Code can use the Playwright MCP (Model Context Protocol) integration.
Checking Playwright MCP Status
To check if Playwright MCP is available:
- Look for browser testing tools in available MCP functions
- Try using browser navigation or screenshot capabilities
Installing Playwright MCP
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.
Using Playwright MCP
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
Project Structure
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
Key Technologies
- 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
Common Tasks
Adding a New Component
- 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
Updating GraphQL Queries
- Modify query in relevant
.graphqlfile - Run codegen to update TypeScript types (if configured)
- Update components using the query
Testing Components
# 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
Debugging
- Use React Developer Tools browser extension
- Redux DevTools for state debugging
- Browser console for network requests
- Source maps are enabled in development mode
Code Style
- 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
Best Practices
Data Fetching
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(...) }, [])
State Management
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
Performance Considerations
- Use React.memo for expensive components
- Implement virtualization for large lists (AG-Grid handles this)
- Lazy load routes and heavy components