Useful components

    Button

    This Button component is from shadcn but we have added two more props for convenience

    import { Button } from "components/ui/button";
    
    const ExamplePage = () => {
      return (
        <Button
          // shows a spinner instead of button content
          isLoading={true}
          // shows loading text with spinner
          loadingText="loading..."
        >
          Click me
        </Button>
      );
    };
    
    export default ExamplePage;
    

    BuyButton

    The BuyButton allows you to make a purchase button of a single product

    import BuyButton from "components/BuyButton";
    
    const ExamplePage = () => {
      return (
        <BuyButton
          // add the product id
          productId={"id_here"}
          // accepts one of two, "payment" or "subscription"
          mode="payment"
        >
          Buy my product
        </BuyButton>
      );
    };
    
    export default ExamplePage;
    

    CheckoutButton

    The CheckoutButton component accepts an array of non subscription products like this

    import { db } from "lib/db";
    import CheckoutButton from "components/CheckoutButton";
    
    const ExamplePage = async () => {
      const products = await db.product.findMany({
        where: { active: true, billingCycle: "ONE_TIME" },
      });
      const [product1, product2] = products;
    
      return (
        <CheckoutButton
          // add as many product as you want usually coming from the cart
          items={[
            { product, product1: 2 },
            { product: product2, quantity: 1 },
          ]}
        >
          Checkout
        </CheckoutButton>
      );
    };
    
    export default ExamplePage;
    

    CopyButton

    The CopyButton component copies text to clipboard

    import { CopyIcon } from "lucide-react";
    import CopyButton from "components/CopyButton";
    
    const ExamplePage = () => {
      return (
        // data prop will be copied to clipboard
        <CopyButton data={"copy this text"}>
          <CopyIcon />
        </CopyButton>
      );
    };
    
    export default ExamplePage;
    

    CTAButton

    The CTAButton will use the product id from .env.local as its product id

    import CTAButton from "components/CTAButton";
    
    const ExamplePage = () => {
      return <CTAButton />;
    };
    
    export default ExamplePage;
    

    NextImage

    The NextImage is similar to <Image /> from Next.js but it will always fill its container, handle non existing images by showing a placeholder, add empty alt if non is added and allows you to view it in lightbox if you add the viewable prop.

    import NextImage from "components/NextImage";
    
    const ExamplePage = () => {
      return (
        <div className="relative">
          <NextImage
            src={"your_image_source"}
            alt={"your_alt_text"}
            // add this prop to maximize the image in lightbox on click
            viewable
          />
        </div>
      );
    };
    
    export default ExamplePage;
    

    Don't forget to configure domain whitelist in next.config.ts file in the root path

    next.config.ts

    const nextConfig: NextConfig = {
      images: {
        remotePatterns: [
          // its better to be specific when using your own s3 bucket
          {
            protocol: "https",
            hostname: "my_bucket.s3.eu-west-2.amazonaws.com",
            // this is important to only optimize this path
            // it helps with cutting costs in the future
            pathname: "/media/**",
          },
          // external sources
          {
            hostname: "picsum.photos",
          },
        ],
        // ...
      },
      // ...
    };
    

    Select

    The Select component uses shadcn component but will make it shorter to implement

    import Select from "components/Select";
    
    const ExamplePage = () => {
      return (
        <Select
          placeholder="Select author"
          data={[
            { label: "Option 1", value: "option-1" },
            { label: "Option 2", value: "option-2" },
          ]}
          value={value}
          onValueChange={onChange}
        />
      );
    };
    
    export default ExamplePage;
    

    RichTextEditor

    The RichTextEditor is a component that allows you to format text with options like bold, italics, headings, lists, links, add images or youtube videos and more.

    It integrates seamlessly with forms and can handle both plain text and HTML content.

    It uses tiptap behind the scene.

    import RichTextEditor from "components/rich-text-editor";
    
    const ExamplePage = () => {
      return (
        <RichTextEditor
          // disabled state
          disabled={isPending}
          // the value
          content={value}
          // on change event
          onChange={onChange}
        />
      );
    };
    
    export default ExamplePage;
    

    AnimateVisibility

    The AnimateVisibility component will allow you to animate the visibility of other components, and has 3 animation modes width, height and both

    import AnimateVisibility from "components/AnimateVisibility";
    import { Button } from "components/ui/button";
    import { useState } from "react";
    
    const ExamplePage = () => {
      const [isVisible, setIsVisible] = useState(false);
    
      return (
        <div>
          <Button onClick={() => setIsVisible(true)}>Show heading</Button>
    
          <AnimateVisibility
            // is the content visible?
            visible={isVisible}
            // animation mode: width | height | both
            mode="width"
          >
            <h5 className="ml-3 font-medium">my heading</h5>
          </AnimateVisibility>
        </div>
      );
    };
    
    export default ExamplePage;
    

    FileUploader

    Uploads files to a specific endpoint it uploads to /api/media/upload endpoint by default

    import FileUploader from "components/FileUploader";
    
    const ExamplePage = () => {
      return (
        <FileUploader
          type="modal" // "modal" | "block";
          endpoint="/endpoint" // optional will use /api/media/upload by default
          allowedFileTypes={[".mp4", ".png"]} // optional will use `uploadAllowedFileTypes` from `utils/constants`
          maxNumberOfFiles={5} // optional will use 1 by default
          visible={false} // is the uploader visible (type="modal" only)
          onClose={() => {}} // a callback to call on close (type="modal" only)
          onUploadComplete={(result) => {
            // upload is complete and the result object will return
            const { successful, failed } = result;
          }}
        />
      );
    };
    
    export default ExamplePage;
    

    LoadingLayout

    <LoadingLayout /> shows a progress bar at the top of the screen to inform users that a page is loading, <LoadingLayout /> works with NextJS's loading.tsx file by marking it as the default export

    /src/app/dashboard/loading.tsx

    import LoadingLayout from "components/LoadingLayout";
    
    export default LoadingLayout;