# How do I make Claude Code reuse my existing components instead of writing new ones?

**Short answer:** Tell it what exists, give it rules it can check, and enforce the rules outside the prompt. Claude Code reads CLAUDE.md at the start of every session, so put a short component inventory there (name, import path, what each one is for) and concrete rules such as "use Button from @/components/ui/button; never write a raw <button>". Ask it to search the codebase before creating any new component. Claude Code's own docs say CLAUDE.md is context, not enforced configuration, so back the rules with checks that fail: a lint rule that forbids raw elements and duplicate imports, a hook that runs the linter after every edit, and a CI job (lint plus Playwright screenshot tests) that blocks the merge when the agent writes its own version anyway.

## Checklist
1. Write a component inventory into CLAUDE.md (or a file it imports with @path, such as @docs/components.md): one line per shared component with its import path and when to use it. Keep it short; Anthropic recommends keeping each CLAUDE.md under about 200 lines.
2. Make the rules concrete and checkable: "All buttons use <Button> from @/components/ui/button", "Modals use <Dialog>", "Colors come from the theme tokens in tailwind.config.ts, no hex values in components". Vague rules like "follow our design system" are easy to ignore.
3. Add a search-first rule: before creating a new component, search src/components for an existing one (by name and by the element it renders) and say which one you're extending; if none fits, explain why before writing a new one.
4. Scope rules to the files they apply to with .claude/rules/ and a paths: field (for example src/components/**/*.tsx), so they load when Claude touches those files instead of filling every session.
5. Enforce with lint: eslint-plugin-react's react/forbid-elements can forbid raw <button>, <input> or <dialog> in app code, and ESLint's no-restricted-imports can block importing a second UI library or an old component path. Point the error message at the component to use.
6. Run the check while Claude works: a Claude Code PostToolUse hook on Edit and Write can run the linter on the changed file and feed the errors back, so the agent fixes them in the same turn.
7. Fail the pull request in CI: run lint and Playwright visual comparisons (expect(page).toHaveScreenshot()) on key pages, so a hand-rolled copy that looks different from the shared component shows up as a diff.
8. When Claude still writes a duplicate, add the missed case to CLAUDE.md or the lint config, not just a chat correction, so the next session knows.

## Why it writes new components
- Each session starts with a fresh context. Unless the inventory is in CLAUDE.md or Claude searches for it, it doesn't know your Button exists, and writing a new one is the shortest path to a working diff.
- Inconsistent naming makes search fail: if the shared component is called PrimaryAction and lives in lib/ui, a search for "button" misses it. The inventory fixes that by mapping purpose to path.
- Conflicting instructions get followed arbitrarily: an old note saying "use MUI" next to a new rule saying "use our components" lets either win. Review and prune the instruction files.

## Instructions vs enforcement
- CLAUDE.md, imports and rules shape what Claude tries to do; they don't guarantee it. Anything that must hold every time belongs in a hook, a lint rule or a CI check.
- Hooks run as shell commands at fixed points (PreToolUse, PostToolUse and others) whatever the model decides, so a lint hook after edits catches raw elements before you review.
- CI is the backstop for every author, human or agent: if the rule matters, the merge should fail without it.

## Ask Rightshold (free, no key)
Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code (one-click setup: https://www.rightshold.com/start?ref=seo), then ask: "Claude Code added two new UI packages instead of using our components. Are their licenses OK for our commercial app?"

```json
{
  "tool": "check_rights_batch",
  "arguments": {
    "intended_use": {
      "commercial_use": true,
      "redistribution": true
    },
    "items": [
      {
        "ref": "<new-ui-package>",
        "license": "MIT"
      },
      {
        "ref": "<new-icon-package>",
        "license": "<its SPDX id>"
      }
    ]
  }
}
```
- No editor? Run curl -fsSL https://www.rightshold.com/check.mjs | node - in your project folder. It checks the direct dependencies in package.json and sends only license ids, never your code or file names.
- license_compatibility checks whether the licenses can be combined in one product and which duties apply together.

_Rightshold does not read your codebase, review pull requests or enforce coding rules; component reuse is enforced by your CLAUDE.md, lint config, hooks and CI. It checks the licenses of packages an agent adds._

## Sources
- [Claude Code docs: How Claude remembers your project (CLAUDE.md, imports, .claude/rules)](https://code.claude.com/docs/en/memory)
- [Claude Code docs: Hooks reference](https://code.claude.com/docs/en/hooks)
- [eslint-plugin-react: forbid-elements](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/forbid-elements.md)
- [ESLint: no-restricted-imports](https://eslint.org/docs/latest/rules/no-restricted-imports)
- [Playwright: Visual comparisons](https://playwright.dev/docs/test-snapshots)

## Related
- [How to stop an AI coding agent from deleting your production database](https://www.rightshold.com/answers/how-to-stop-an-ai-coding-agent-from-deleting-your-production-database)
- [How to check licenses of all npm dependencies before shipping](https://www.rightshold.com/answers/how-to-check-licenses-of-all-npm-dependencies-before-shipping)
- [How to vet an MCP server before installing it](https://www.rightshold.com/answers/how-to-vet-an-mcp-server-before-installing-it)

_Not legal advice. Updated 2026-10-29. JSON: https://www.rightshold.com/answers/how-to-make-claude-code-reuse-your-existing-components.json_