Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,26 @@ on:
pull_request:
branches: [main]
jobs:
check:
registry:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Repository sanity check
run: test -f README.md
- uses: pnpm/action-setup@v4
with:
version: 11
- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm typecheck
- name: Lint
run: pnpm lint
- name: Test
run: pnpm test
- name: Build registry
run: pnpm build
- name: Built registry output must be committed and in sync
run: git diff --exit-code r/
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
*.log
.DS_Store
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,51 @@ Full, copy-paste-ready snippets for every stack live in the docs: **[docs.logo.d

---

## React components (shadcn/ui)

For React apps, this repo is also the official Logo.dev [shadcn/ui](https://ui.shadcn.com) registry. It ships production-grade components for the logo patterns most apps end up rebuilding by hand: image fallbacks, dark mode, retina, and debounced company search.

Install a component with the shadcn CLI:

```bash
npx shadcn@latest add https://www.logo.dev/r/logo.json
```

You can also install straight from this repo, pinned to a branch, tag, or commit:

```bash
npx shadcn@latest add logo-dev/logo-api/logo
```

| Component | What you get |
| --- | --- |
| `logo` | A logo that never breaks: domain/name/ticker/crypto/ISIN lookup, retina srcSet, automatic dark-mode variants, monogram/initials/custom fallbacks |
| `logo-avatar` | A logo in a shadcn Avatar shell with initials fallback — CRM rows, transaction feeds |
| `brand-search` | Company autocomplete combobox backed by the Search API, with a Next.js route that keeps your secret key server-side |
| `logo-wall` | Customer/integration logo grid from a list of domains, grayscale-to-color hover |
| `attribution` | The attribution link free plans require in production |
| `logo-lib` | The typed URL builder underneath all of the above, useful on its own |

Components read your publishable key from `NEXT_PUBLIC_LOGO_DEV_TOKEN`; `brand-search` also needs `LOGO_DEV_SECRET_KEY` on the server. Both are added to `.env.local` on install. Full guide: **[shadcn/ui components →](https://docs.logo.dev/integrations/shadcn)**

<details>
<summary><strong>Developing the registry</strong></summary>

<br />

```bash
pnpm install
pnpm test # URL builder unit tests
pnpm typecheck
pnpm build # shadcn build → r/*.json (committed; CI checks it's in sync)
```

Component sources live in `registry/new-york/`. `components/ui/` holds vendored shadcn primitives used only for typechecking — consumers get those from ui.shadcn.com.

</details>

---

## Migrating from Clearbit

**Clearbit's Logo API shut down on December 8, 2025.** If your app still points at `logo.clearbit.com`, the logos are broken.
Expand Down
43 changes: 43 additions & 0 deletions biome.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"extends": ["ultracite/biome/core", "ultracite/biome/react"],
"files": {
// `r/` is generated by `shadcn build` (pnpm build); never lint output.
// `assets/` holds static brand SVGs, not source to format.
"includes": ["!!r", "!!assets"]
},
"linter": {
"rules": {
"style": {
"useFilenamingConvention": "off",
"useNamingConvention": "off",
"noMagicNumbers": "off"
}
}
},
"overrides": [
{
// Vendored verbatim from shadcn/ui for typechecking the registry
// sources. Never distributed by this registry (consumers pull these
// primitives from ui.shadcn.com), so they aren't held to our rules.
"includes": ["components/ui/**"],
"linter": {
"enabled": false
}
},
{
// Registry sources ship verbatim into consumer projects. Raw <img> is
// deliberate (img.logo.dev is already an image CDN; next/image would
// couple consumers to Next.js), and repo-level overrides keep inline
// suppression comments out of the code customers receive.
"includes": ["registry/**"],
"linter": {
"rules": {
"performance": {
"noImgElement": "off"
}
}
}
}
]
}
20 changes: 20 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"css": "styles/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
50 changes: 50 additions & 0 deletions components/ui/avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";

import * as AvatarPrimitive from "@radix-ui/react-avatar";
import * as React from "react";

import { cn } from "@/lib/utils";

const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
ref={ref}
{...props}
/>
));
Avatar.displayName = AvatarPrimitive.Root.displayName;

const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
className={cn("aspect-square h-full w-full", className)}
ref={ref}
{...props}
/>
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;

const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className
)}
ref={ref}
{...props}
/>
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;

export { Avatar, AvatarFallback, AvatarImage };
57 changes: 57 additions & 0 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";

import { cn } from "@/lib/utils";

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
defaultVariants: {
size: "default",
variant: "default",
},
variants: {
size: {
default: "h-9 px-4 py-2",
icon: "h-9 w-9",
lg: "h-10 rounded-md px-8",
sm: "h-8 rounded-md px-3 text-xs",
},
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
},
},
}
);

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ className, size, variant }))}
ref={ref}
{...props}
/>
);
}
);
Button.displayName = "Button";

export { Button, buttonVariants };
Loading
Loading