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;