diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5375053cf..021b7796f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,15 +6,6 @@ Read the [example PR](https://github.com/magicuidesign/magicui/pull/12) to learn Once done, open a pull request from your forked repo to the main repo [here](https://github.com/magicuidesign/magicui/compare). -- [Getting Started](#getting-started) - - [Fork and Clone the Repository](#fork-and-clone-the-repository) - - [Adding a New Component](#adding-a-new-component) - - [1. Add Sidebar Button Meta for Your Component](#1-add-sidebar-button-meta-for-your-component) - - [2. Create Your Component](#2-create-your-component) - - [3. Create a Basic Example Showcasing Your Component](#3-create-a-basic-example-showcasing-your-component) - - [4. Create MDX Page for Your Component](#4-create-mdx-page-for-your-component) - - [5. Add Registry Export](#5-add-registry-export) - ## Getting Started ### Fork and Clone the Repository @@ -61,89 +52,98 @@ Once done, open a pull request from your forked repo to the main repo [here](htt To add a new component to MagicUI, you will need to modify several files. Follow these steps: -### 1. Add Sidebar Button Meta for Your Component - -**File:** `config/docs.ts` - -Add metadata for your component in the sidebar navigation. - -```typescript -// Add sidebar button meta for your component -{ - title: "Example Component", - href: `/docs/components/example-component`, - items: [], - label: "New", -} -``` - -### 2. Create Your Component - -**File:** `registry/components/magicui/example-component.tsx` +### 1. Create Component -Create the main component file. +Create the main component in `registry/components/magicui/example-component.tsx` ```typescript -// Create your component here import React from 'react' -const ExampleComponent = () => { +export default function ExampleComponent() { return (
This is your component.
) } - -export default ExampleComponent; ``` -### 3. Create a Basic Example Showcasing Your Component - -**File:** `registry/components/example/example-component-demo.tsx` +### 2. Create Component Demo -Provide a basic example to showcase your component. +Provide a basic example to showcase your component in `registry/components/example/example-component-demo.tsx` ```typescript -// Create a very basic example showcasing your component import ExampleComponent from '@/registry/components/magicui/example-component' -const ExampleComponentDemo = () => ( -
+export default function ExampleComponentDemo() { + return ( +
-) - -export default ExampleComponentDemo; + ) +} ``` -### 4. Create MDX Page for Your Component +### 3. Update Sidebar -**File:** `content/docs/components/example-component.mdx` +Add your component to the sidebar in `config/docs.ts` -Create an MDX file for documenting your component. +```typescript +{ + title: "Example Component", + href: `/docs/components/example-component`, + items: [], + label: "New", +} +``` + +### 4. Create docs + +Create an MDX file for documenting your component in `content/docs/components/example-component.mdx` ````md --- title: Example Component date: 2024-06-01 -description: Example component for demonstrating MagicUI integration -author: Bankkroll +description: Example component for Magic UI +author: magicui published: true --- - +## Installation -### Installation + -Copy and paste the following code into your project. + + CLI + Manual + + -```text -components/magicui/example-component.tsx +```bash +npx magicui-cli add example-component ``` + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + @@ -153,21 +153,14 @@ components/magicui/example-component.tsx | Prop | Type | Description | Default | | ----- | ------ | -------------------------- | ------- | | color | String | The color of the component | "blue" | - -## Credits - -- Credit to [Bankk](https://www.x.com/bankkroll_eth) ```` -### 5. Add Registry Export - -**File:** `registry/index.tsx` +### 5. Update Registry -Export your component and example in the registry. +Export your component and example in the registry in `registry/index.tsx` ```typescript const ui: Registry = { - // other components "example-component": { name: "example-component", type: "components:ui", @@ -176,7 +169,6 @@ const ui: Registry = { }; const example: Registry = { - // other examples "example-component-demo": { name: "example-component", type: "components:example", @@ -188,48 +180,49 @@ const example: Registry = { }; ``` -## How to use CLI +## Adding to the showcase -1. Run CLI script from project `root` folder +## 1. Create your showcase as a MDX file -```bash -pnpm run install:cli -``` +Create your showcase in `content/showcase/website-name.mdx` -```bash -pnpm run dev:cli +```mdx +--- +title: website-name.com +description: Website description +image: /showcase/website-name.png +href: https://website-name.com +featured: true +affiliation: YC S25, raised $10M +--- ``` -```bash -pnpm run build:cli -``` +## 2. Create an image -```bash -pnpm run release:cli -``` +Upload an image of your site to `public/showcase/website-name.png` -2. Run CLI script from `/packages/cli` folder +## How to use CLI + +1. Run CLI script from project `root` folder ```bash -pnpm run install +pnpm run install:cli ``` ```bash -pnpm run dev +pnpm run dev:cli ``` ```bash -pnpm run build +pnpm run build:cli ``` ```bash -pnpm run release +pnpm run release:cli ``` -Lots more useful scripts are available in package.json - The CLI in development uses index.json from default `3000` port on localhost. Otherwise [https://magicui.design](https://magicui.design/registry/index.json) ## Ask for Help -For any help or questions, please open a new GitHub issue and we will get back to you :) +For any help or questions, please open a new GitHub issue. diff --git a/app/(marketing)/page.tsx b/app/(marketing)/page.tsx index 57f13cf58..961508de5 100644 --- a/app/(marketing)/page.tsx +++ b/app/(marketing)/page.tsx @@ -1,30 +1,15 @@ -import Hero from "@/components/hero"; -import { Mdx } from "@/components/mdx-components"; - -import "@/styles/mdx.css"; - -import { notFound } from "next/navigation"; -import { allPages } from "contentlayer/generated"; - -import Testimonials from "@/components/testimonials"; - -const PAGE = "home"; +import ComponentDemos from "@/components/sections/component-demos"; +import Hero from "@/components/sections/hero"; +import Showcase from "@/components/sections/showcase"; +import Testimonials from "@/components/sections/testimonials"; export default async function Home() { - const page = allPages.find((page) => page.slugAsParams === PAGE); - - if (!page) { - notFound(); - } - return ( <> -
- -
+ + - {/* */} ); } diff --git a/app/(marketing)/showcase/[...slug]/page.tsx b/app/(marketing)/showcase/[...slug]/page.tsx new file mode 100644 index 000000000..946deb53f --- /dev/null +++ b/app/(marketing)/showcase/[...slug]/page.tsx @@ -0,0 +1,98 @@ +import { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { env } from "@/env.mjs"; +import { allShowcases } from "contentlayer/generated"; + +import { siteConfig } from "@/config/site"; +import { absoluteUrl } from "@/lib/utils"; +import { ShowcaseCard } from "@/components/sections/showcase"; + +interface PageProps { + params: { + slug: string[]; + }; +} + +async function getPageFromParams(params: PageProps["params"]) { + const slug = params?.slug?.join("/"); + const page = allShowcases.find((page) => page.slugAsParams === slug); + + if (!page) { + null; + } + + return page; +} + +export async function generateMetadata({ + params, +}: PageProps): Promise { + const page = await getPageFromParams(params); + + if (!page) { + return {}; + } + + const url = env.NEXT_PUBLIC_APP_URL; + + const ogUrl = new URL(`${url}/api/og`); + ogUrl.searchParams.set("heading", page.title); + ogUrl.searchParams.set("type", siteConfig.name); + ogUrl.searchParams.set("mode", "light"); + + return { + title: page.title, + description: page.description, + openGraph: { + title: page.title, + description: page.description, + type: "article", + url: absoluteUrl(page.slug), + images: [ + { + url: ogUrl.toString(), + width: 1200, + height: 630, + alt: page.title, + }, + ], + }, + twitter: { + card: "summary_large_image", + title: page.title, + description: page.description, + images: [ogUrl.toString()], + }, + }; +} + +export async function generateStaticParams(): Promise { + return allShowcases.map((page) => ({ + slug: page.slugAsParams.split("/"), + })); +} + +export default async function PagePage({ params }: PageProps) { + const page = await getPageFromParams(params); + + if (!page) { + notFound(); + } + + return ( +
+

+ {page.title} +

+

+ {page.title} uses Magic UI to build their landing page. +

+ +
+ ); +} diff --git a/app/(marketing)/showcase/page.tsx b/app/(marketing)/showcase/page.tsx new file mode 100644 index 000000000..6149af61b --- /dev/null +++ b/app/(marketing)/showcase/page.tsx @@ -0,0 +1,24 @@ +import { allShowcases } from "contentlayer/generated"; + +import { ShowcaseCard } from "@/components/sections/showcase"; +import BlurFade from "@/registry/components/magicui/blur-fade"; + +export default async function Page() { + return ( +
+

+ Showcase +

+

+ Companies choose Magic UI to build their landing pages. +

+
+ {allShowcases.map((showcase, idx) => ( + + + + ))} +
+
+ ); +} diff --git a/components/sections/component-demos.tsx b/components/sections/component-demos.tsx new file mode 100644 index 000000000..3f6a654fb --- /dev/null +++ b/components/sections/component-demos.tsx @@ -0,0 +1,29 @@ +import { Mdx } from "@/components/mdx-components"; + +import "@/styles/mdx.css"; + +import { notFound } from "next/navigation"; +import { allPages } from "contentlayer/generated"; + +const PAGE = "home"; + +export default function ComponentDemos() { + const page = allPages.find((page) => page.slugAsParams === PAGE); + + if (!page) { + notFound(); + } + + return ( +
+

+ Component Demos +

+

+ Here are some of the components that you can use to build your landing + pages. +

+ +
+ ); +} diff --git a/components/cta-section.tsx b/components/sections/cta.tsx similarity index 100% rename from components/cta-section.tsx rename to components/sections/cta.tsx diff --git a/components/hero-client.tsx b/components/sections/hero-client.tsx similarity index 98% rename from components/hero-client.tsx rename to components/sections/hero-client.tsx index bee0ba7d3..fccc53ec5 100644 --- a/components/hero-client.tsx +++ b/components/sections/hero-client.tsx @@ -62,6 +62,7 @@ export default function HeroClient({ post }: { post: Doc }) { Framer Motion .
+ Perfect companion for shadcn/ui.

@@ -99,7 +100,7 @@ export default function HeroClient({ post }: { post: Doc }) {
-
+
diff --git a/components/hero.tsx b/components/sections/hero.tsx similarity index 91% rename from components/hero.tsx rename to components/sections/hero.tsx index a08c01114..1bd39fe97 100644 --- a/components/hero.tsx +++ b/components/sections/hero.tsx @@ -1,7 +1,7 @@ import { allDocs } from "@/.contentlayer/generated"; import { compareDesc } from "date-fns"; -import HeroClient from "@/components/hero-client"; +import HeroClient from "@/components/sections/hero-client"; export default async function Hero() { const post = allDocs diff --git a/components/sections/showcase.tsx b/components/sections/showcase.tsx new file mode 100644 index 000000000..8e09a5d96 --- /dev/null +++ b/components/sections/showcase.tsx @@ -0,0 +1,64 @@ +import Image from "next/image"; +import Link from "next/link"; +import { allShowcases } from "@/.contentlayer/generated"; +import { ChevronRightIcon } from "@radix-ui/react-icons"; + +import Marquee from "@/registry/components/magicui/marquee"; + +export interface ShowcaseCardProps { + title: string; + image: string; + href: string; + affiliation?: string; +} +export function ShowcaseCard({ + title, + image, + href, + affiliation, +}: ShowcaseCardProps) { + return ( + + {title} + +
+
+ {title} + +
+

{affiliation}

+
+ + ); +} + +export default async function Showcase() { + return ( +
+

+ Showcase +

+

+ Companies choose Magic UI to build their landing pages. +

+
+ + {allShowcases.map((showcase, idx) => ( + + ))} + +
+
+
+
+ ); +} diff --git a/components/testimonials.tsx b/components/sections/testimonials.tsx similarity index 100% rename from components/testimonials.tsx rename to components/sections/testimonials.tsx diff --git a/config/docs.ts b/config/docs.ts index 3d24a71bb..db7046566 100644 --- a/config/docs.ts +++ b/config/docs.ts @@ -11,6 +11,10 @@ export const docsConfig: DocsConfig = { title: "Components", href: "/components", }, + { + title: "Showcase", + href: "/showcase", + }, { title: "Templates", href: "https://pro.magicui.design", @@ -93,6 +97,11 @@ export const docsConfig: DocsConfig = { { title: "Landing Page Components", items: [ + { + title: "Marquee", + href: `/docs/components/marquee`, + items: [], + }, { title: "Bento Grid", href: `/docs/components/bento-grid`, @@ -103,11 +112,6 @@ export const docsConfig: DocsConfig = { href: `/docs/components/animated-list`, items: [], }, - { - title: "Marquee", - href: `/docs/components/marquee`, - items: [], - }, { title: "Dock", href: `/docs/components/dock`, diff --git a/content/showcase/cognosys.mdx b/content/showcase/cognosys.mdx new file mode 100644 index 000000000..28a15aa3e --- /dev/null +++ b/content/showcase/cognosys.mdx @@ -0,0 +1,9 @@ +--- +title: cognosys.ai +description: Cognosys is a platform for building and deploying AI agents +image: /showcase/cognosys.png +href: https://cognosys.ai +featured: true +author: "@cognosysai" +affiliation: Backed by Google Ventures, raised $2M +--- diff --git a/content/showcase/infisical.mdx b/content/showcase/infisical.mdx new file mode 100644 index 000000000..9afca8962 --- /dev/null +++ b/content/showcase/infisical.mdx @@ -0,0 +1,8 @@ +--- +title: infisical.com +description: Infisical is a platform for managing secrets. +image: /showcase/infisical.png +href: https://infisical.com +featured: true +affiliation: YC W23, raised $2.8M +--- diff --git a/content/showcase/langfuse.mdx b/content/showcase/langfuse.mdx new file mode 100644 index 000000000..fc343ca74 --- /dev/null +++ b/content/showcase/langfuse.mdx @@ -0,0 +1,8 @@ +--- +title: langfuse.com +description: Open Source LLM Engineering Platform +image: /showcase/langfuse.png +href: https://langfuse.com +featured: true +affiliation: YC W23, raised $4M +--- diff --git a/content/showcase/llmreport.mdx b/content/showcase/llmreport.mdx new file mode 100644 index 000000000..809655cb2 --- /dev/null +++ b/content/showcase/llmreport.mdx @@ -0,0 +1,8 @@ +--- +title: llm.report +description: LLM Report is an LLM observability platform +image: /showcase/llmreport.png +href: https://llm.report +featured: true +affiliation: Vercel AI Accelerator 2023, won $120k +--- diff --git a/content/showcase/million.mdx b/content/showcase/million.mdx new file mode 100644 index 000000000..5b2e28f56 --- /dev/null +++ b/content/showcase/million.mdx @@ -0,0 +1,8 @@ +--- +title: million.dev +description: Million.dev makes websites fast. +image: /showcase/million.png +href: https://million.dev +featured: true +affiliation: YC W24 +--- diff --git a/content/showcase/unriddle.mdx b/content/showcase/unriddle.mdx new file mode 100644 index 000000000..da1063d24 --- /dev/null +++ b/content/showcase/unriddle.mdx @@ -0,0 +1,8 @@ +--- +title: unriddle.ai +description: Unriddle.ai is part of buildspace sf1, pioneer, and YC S24 +image: /showcase/unriddle.png +href: https://unriddle.ai +featured: true +affiliation: YC S24 +--- diff --git a/contentlayer.config.ts b/contentlayer.config.ts index bd38dfe72..b5b9e8410 100644 --- a/contentlayer.config.ts +++ b/contentlayer.config.ts @@ -53,6 +53,72 @@ const computedFields = { }, }; +export const Showcase = defineDocumentType(() => ({ + name: "Showcase", + filePathPattern: `showcase/**/*.mdx`, + contentType: "mdx", + fields: { + title: { + type: "string", + required: true, + }, + author: { + type: "string", + required: false, + }, + description: { + type: "string", + }, + image: { + type: "string", + required: true, + }, + href: { + type: "string", + required: true, + }, + affiliation: { + type: "string", + required: true, + }, + featured: { + type: "boolean", + default: false, + required: false, + }, + }, + computedFields: { + slug: { + type: "string", + resolve: (doc: any) => `/${doc._raw.flattenedPath}`, + }, + slugAsParams: { + type: "string", + resolve: (doc: any) => + doc._raw.flattenedPath.split("/").slice(1).join("/"), + }, + structuredData: { + type: "json", + resolve: (doc: any) => + ({ + "@context": "https://schema.org", + "@type": `BlogPosting`, + headline: doc.title, + datePublished: doc.date, + dateModified: doc.date, + description: doc.summary, + image: doc.image, + url: `https://magicui.design/${doc._raw.flattenedPath}`, + author: { + "@type": "Person", + name: doc.author, + url: `https://twitter.com/${doc.author}`, + }, + }) as WithContext, + }, + }, +})); + export const Page = defineDocumentType(() => ({ name: "Page", filePathPattern: `pages/**/*.mdx`, @@ -119,7 +185,7 @@ export const Doc = defineDocumentType(() => ({ export default makeSource({ contentDirPath: "./content", - documentTypes: [Page, Doc], + documentTypes: [Page, Doc, Showcase], mdx: { remarkPlugins: [remarkGfm], rehypePlugins: [ diff --git a/public/showcase/cognosys.png b/public/showcase/cognosys.png new file mode 100644 index 000000000..8d07fc4ef Binary files /dev/null and b/public/showcase/cognosys.png differ diff --git a/public/showcase/infisical.png b/public/showcase/infisical.png new file mode 100644 index 000000000..fff222722 Binary files /dev/null and b/public/showcase/infisical.png differ diff --git a/public/showcase/langfuse.png b/public/showcase/langfuse.png new file mode 100644 index 000000000..4726c7b1c Binary files /dev/null and b/public/showcase/langfuse.png differ diff --git a/public/showcase/llmreport.png b/public/showcase/llmreport.png new file mode 100644 index 000000000..dfa637b23 Binary files /dev/null and b/public/showcase/llmreport.png differ diff --git a/public/showcase/million.png b/public/showcase/million.png new file mode 100644 index 000000000..7b9896aee Binary files /dev/null and b/public/showcase/million.png differ diff --git a/public/showcase/unriddle.png b/public/showcase/unriddle.png new file mode 100644 index 000000000..4b3bb5be5 Binary files /dev/null and b/public/showcase/unriddle.png differ