all articles
tech · 6 min read
AI-assisted UI development with shadcn/ui components
Build interfaces with shadcn/ui and AI assistance while checking accessibility, dependency licenses and generated code before anything ships.
AI coding assistants are good at producing interface code, and shadcn/ui is a popular base for that code because the components live in your own repository. That combination can get a working screen in front of you quickly. Whether it is fast to ship depends on what you check before release: accessibility, dependencies and the generated code itself.
What shadcn/ui is, and why it suits AI-assisted work
The shadcn/ui documentation describes the project plainly: "This is not a component library. It is how you build your component library." Instead of installing a package and importing a black box, you use its CLI to add a component, and the component's source code is written into your project. You own that code and can change it.
The docs list "AI-Ready" as a design goal, described as "Open code for LLMs to read, understand, and improve." In practice this matters because an AI assistant working in your editor can see the actual button, dialog or form component it is composing, rather than guessing at a library's API.
The source repository on GitHub is published under the MIT license.
The same property cuts the other way. Because the components are your code, nobody upstream fixes them for you after you change them, and an AI assistant can edit them as freely as it edits anything else.
Getting started with the current CLI
The CLI is published as the shadcn package. The docs show these commands to set up an existing project and add a component:
npx shadcn@latest init
npx shadcn@latest add button
The CLI also has a view command to preview registry items before installing, and a docs command that fetches documentation for a component. Commands and options change between releases, so check the CLI page rather than an old tutorial or an AI assistant's memory. An assistant trained on older material may suggest commands or flags that have since changed.
After add, the component appears in your project, typically imported as @/components/ui/button in the docs' examples. Commit it on its own before you or the AI modify it. That gives you a clean baseline diff for every later change.
A working pattern for AI-assisted UI
- Add the shadcn/ui components you need with the CLI and commit them untouched.
- Ask the assistant for one screen or one component at a time, and tell it which existing components to use. "Build the settings form using our Input, Label and Button components from components/ui" produces more consistent code than "make a settings page".
- Review the diff. Look for new packages, copied-in styles that duplicate your theme, and edits to the shared components in components/ui that you did not ask for.
- Run the accessibility and test checks below.
- Merge only after someone other than the prompter has looked at it in a browser.
Accessibility checks that AI-generated UI often misses
The shadcn/ui docs describe the components as accessible. That is a property of the components as shipped. Generated code that uses them can still break accessibility, and these are the gaps to look for:
- Icon-only buttons with no accessible name. A trash-can icon means nothing to a screen reader unless the button has a label.
- Form inputs without a connected label, or with placeholder text used as the only label.
- Clickable divs instead of buttons or links, which cannot be reached with the keyboard.
- Links styled as buttons that end up with the wrong role. The Button page in the docs specifically warns about one such pattern and recommends applying the button styles to a plain link instead.
- Color used as the only signal for errors or status.
- Focus that disappears after a dialog closes, or no visible focus style at all.
Test with the keyboard alone: tab through the screen, open and close every dialog, submit every form. Then run an automated checker in your browser's developer tools. Neither replaces testing with real assistive technology for important flows, but together they catch most of what AI-generated markup gets wrong.
A tested component example
Here is a small component built on the shadcn/ui Button, with a test that checks the thing AI output most often forgets: an accessible name on an icon-only button.
// components/delete-row-button.tsx
import { Trash2 } from "lucide-react"
import { Button } from "@/components/ui/button"
type Props = {
itemName: string
onDelete: () => void
}
export function DeleteRowButton({ itemName, onDelete }: Props) {
return (
<Button
variant="ghost"
size="icon"
aria-label={`Delete ${itemName}`}
onClick={onDelete}
>
<Trash2 aria-hidden="true" />
</Button>
)
}
// components/delete-row-button.test.tsx
import { render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { expect, test, vi } from "vitest"
import { DeleteRowButton } from "./delete-row-button"
test("has an accessible name and calls onDelete", async () => {
const onDelete = vi.fn()
render(<DeleteRowButton itemName="Invoice 42" onDelete={onDelete} />)
const button = screen.getByRole("button", { name: "Delete Invoice 42" })
await userEvent.click(button)
expect(onDelete).toHaveBeenCalledTimes(1)
})
The test finds the button by its role and accessible name, which is the query Testing Library's docs recommend as "your top preference for just about everything." If someone, or an assistant, later removes the label, the test fails. The example assumes Vitest, Testing Library and the lucide-react icon set are already in the project; adapt it to whatever test runner and icon set you use. Variant and size names come from the current Button docs, so confirm them against the version you installed.
Dependency and license review
Every shadcn add and every AI suggestion can bring in packages. Before merging:
- Read the dependency changes in your package manifest and lockfile diff. Ask why each new package is there.
- Check each new package's license against what your business can accept. MIT on shadcn/ui itself does not tell you the license of what a component pulls in.
- Check maintenance: recent releases, open security advisories and who publishes it.
- Run your package manager's audit command and read the result rather than auto-fixing.
- Reject packages the assistant added to solve something your existing components already handle.
Reviewing the generated code itself
| Check | What to look for | What to do |
|---|---|---|
| Shared components changed | Edits inside components/ui | Revert unless intended, then make the change once on purpose |
| Hardcoded styles | Colors and spacing outside your theme | Replace with your existing tokens and classes |
| Duplicate components | A second, slightly different button or card | Use the existing one |
| Data handling | Fetch calls, keys or user data in UI files | Move to the right layer and remove secrets |
| Unused code | Props, imports and states nothing uses | Delete before merge |
These checks cost far less than reworking an interface after release.
Where automotif fits
If your team builds internal tools or client-facing screens and wants a repeatable way to use AI assistants with shadcn/ui, AI enablement is the closest fit: training on your real interface tasks plus a written playbook for prompting, review and testing. To see whether it suits your team, book a free call of 30 to 60 minutes.