├── .env.example ├── .eslintrc.json ├── .gitignore ├── Demo-App.png ├── README.md ├── components.json ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public ├── next.svg └── vercel.svg ├── src ├── app │ ├── actions.ts │ ├── apple-icon.png │ ├── favicon.ico │ ├── globals.css │ ├── icon.png │ ├── layout.tsx │ └── page.tsx ├── components │ ├── home │ │ ├── CopyLabel.tsx │ │ ├── Output.tsx │ │ └── UserInput.tsx │ ├── icons │ │ ├── Meta.tsx │ │ └── Mistral.tsx │ ├── magicui │ │ ├── animated-gradient-text.tsx │ │ ├── border-beam.tsx │ │ └── grid-pattern.tsx │ └── ui │ │ ├── alert-dialog.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── form.tsx │ │ ├── hover-card.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── select.tsx │ │ ├── skeleton.tsx │ │ ├── slider.tsx │ │ ├── switch.tsx │ │ ├── textarea.tsx │ │ └── tooltip.tsx ├── context │ └── BioContext.tsx └── lib │ └── utils.ts ├── tailwind.config.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | GROQ_API_KEY="your groq API key" -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /Demo-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/AI-Powered-Twitter-Bio-Generator/d9967d8609c5b0d92f21bcc9973cb16a4a6d09ff/Demo-App.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js AI powered twitter Bio Generator Using Groq | Shadcn | Llama 3 🔥 2 | 3 | ![GitHub stars](https://img.shields.io/github/stars/codebucks27/AI-Powered-Twitter-Bio-Generator?style=social&logo=ApacheSpark&label=Stars)   4 | ![GitHub forks](https://img.shields.io/github/forks/codebucks27/AI-Powered-Twitter-Bio-Generator?style=social&logo=KashFlow&maxAge=3600)   5 | ![Github Followers](https://img.shields.io/github/followers/codebucks27.svg?style=social&label=Follow)  
6 | 7 | This repository contains **final code** for Next.js AI powered twitter Bio Generator.
8 | 9 | For Demo checkout following link👇:
10 | [Next.js AI powered twitter Bio Generator Website Demo](https://ai-powered-twitter-bio-generator.vercel.app/)
11 | 12 | If you want to learn how to create it please follow below tutorial👇:
13 | ➡ Tutorial Link 💚: [Build an AI powered twitter Bio Generator Using Next.js and Groq | Shadcn | Llama 3](https://youtu.be/pgnfQvkPyNQ) 14 | [![YouTube Video Views](https://img.shields.io/youtube/views/pgnfQvkPyNQ 15 | )](https://youtu.be/pgnfQvkPyNQ)
16 | 17 | 💚 Checkout my personal website [DevDreaming](https://devdreaming.com)
18 | 19 | --- 20 | # ⭐DO NOT FORGET TO STAR THIS REPO⭐ 21 | --- 22 | 23 | ## Images of The AI App: 24 | 25 | ![Next.js AI powered twitter Bio Generator Website Demo](https://github.com/codebucks27/AI-Powered-Twitter-Bio-Generator/blob/main/Demo-App.png) 26 | 27 | 28 | ## Resources Used in This Project 29 | 30 | #### Development Resources 31 | 32 | - Font from [Vercel Fonts](https://vercel.com/font/)
33 | - Icons from [Iconify Design](https://icon-sets.iconify.design/)
34 | - LLMs from [Groq](https://groq.com/)
35 | - UI components using [Shadcn UI](https://ui.shadcn.com/) and [Magic UI](https://magicui.design/)
36 | - AI integration using [Vercel AI SDK](https://sdk.vercel.ai/)
37 | 38 | 39 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 40 | 41 | ## Getting Started 42 | 43 | First, run the development server: 44 | 45 | ```bash 46 | npm run dev 47 | # or 48 | yarn dev 49 | # or 50 | pnpm dev 51 | # or 52 | bun dev 53 | ``` 54 | 55 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 56 | 57 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 58 | 59 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 60 | 61 | ## Learn More 62 | 63 | To learn more about Next.js, take a look at the following resources: 64 | 65 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 66 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 67 | 68 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 69 | 70 | ## Deploy on Vercel 71 | 72 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 73 | 74 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 75 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | compiler: { 4 | removeConsole: true, 5 | }, 6 | }; 7 | 8 | export default nextConfig; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitter-bio-generator", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "author": "CodeBucks (https://devdreaming.com)", 12 | "dependencies": { 13 | "@ai-sdk/openai": "^0.0.14", 14 | "@hookform/resolvers": "^3.4.2", 15 | "@radix-ui/react-alert-dialog": "^1.0.5", 16 | "@radix-ui/react-hover-card": "^1.0.7", 17 | "@radix-ui/react-label": "^2.0.2", 18 | "@radix-ui/react-select": "^2.0.0", 19 | "@radix-ui/react-slider": "^1.1.2", 20 | "@radix-ui/react-slot": "^1.0.2", 21 | "@radix-ui/react-switch": "^1.0.3", 22 | "@radix-ui/react-tooltip": "^1.0.7", 23 | "ai": "^3.1.14", 24 | "class-variance-authority": "^0.7.0", 25 | "clsx": "^2.1.1", 26 | "endent": "^2.1.0", 27 | "geist": "^1.3.0", 28 | "lucide-react": "^0.378.0", 29 | "next": "14.2.26", 30 | "react": "^18", 31 | "react-dom": "^18", 32 | "react-hook-form": "^7.51.5", 33 | "tailwind-merge": "^2.3.0", 34 | "tailwindcss-animate": "^1.0.7", 35 | "zod": "^3.23.8" 36 | }, 37 | "devDependencies": { 38 | "@types/node": "^20.12.12", 39 | "@types/react": "^18.3.2", 40 | "@types/react-dom": "^18", 41 | "eslint": "^8", 42 | "eslint-config-next": "14.1.4", 43 | "postcss": "^8", 44 | "tailwindcss": "^3.4.1", 45 | "typescript": "^5" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/actions.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { generateObject } from "ai"; 4 | import { createOpenAI } from "@ai-sdk/openai"; 5 | import { z } from "zod"; 6 | import endent from "endent"; 7 | 8 | const groq = createOpenAI({ 9 | apiKey: process.env.GROQ_API_KEY ?? "", 10 | baseURL: "https://api.groq.com/openai/v1", 11 | }); 12 | 13 | const systemPrompt = endent` 14 | You are an AI assistant tasked with generating Twitter bios based on user input. 15 | 16 | Instructions: 17 | 18 | Analyze the User's Inputs: 19 | - Carefully review the provided tone and bio type. 20 | - Understand the user's core focus and primary activities. 21 | 22 | Generate the Bio: 23 | 24 | - Create a bio that succinctly answers: 25 | - Who is the user? 26 | - What does the user do? 27 | - What can others expect from the user? 28 | - Reflect the given 'Bio Tone' and 'Bio Type' in the style and language of the bio. Do not explicitly mention the tone or type. 29 | 30 | Bio Requirements: 31 | 32 | - Use an informal and approachable tone. 33 | - Do not include hashtags or any words start with #. 34 | - Highlight the most important information about the user. 35 | - Avoid using too many buzzwords or overdoing humor. 36 | - Ensure the bio length is between 120 and 160 characters. 37 | - Provide at least four different bio options. 38 | - If 'Add Emojis' is true, include relevant emojis; if false, you must include any emojis. 39 | - The response must be in JSON format 40 | 41 | Additional Guidelines: 42 | - Maintain clarity and coherence in each bio. 43 | - Provide response in JSON format only 44 | 45 | Do not include any description, do not include the \`\`\`. 46 | Code (no \`\`\`): 47 | `; 48 | 49 | export async function generateBio( 50 | input: string, 51 | temperature: number, 52 | model: string 53 | ) { 54 | "use server"; 55 | 56 | const { 57 | object: data, 58 | warnings, 59 | finishReason, 60 | rawResponse, 61 | } = await generateObject({ 62 | model: groq(model), 63 | system: systemPrompt, 64 | prompt: input, 65 | temperature: temperature, 66 | maxTokens: 1024, 67 | schema: z.object({ 68 | data: z.array( 69 | z.object({ 70 | bio: z.string().describe("Add generated bio here!"), 71 | }) 72 | ), 73 | }), 74 | }); 75 | // console.log(warnings, finishReason, rawResponse); 76 | 77 | return { data }; 78 | } 79 | -------------------------------------------------------------------------------- /src/app/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/AI-Powered-Twitter-Bio-Generator/d9967d8609c5b0d92f21bcc9973cb16a4a6d09ff/src/app/apple-icon.png -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/AI-Powered-Twitter-Bio-Generator/d9967d8609c5b0d92f21bcc9973cb16a4a6d09ff/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 240 10% 3.9%; 9 | 10 | --card: 0 0% 100%; 11 | --card-foreground: 240 10% 3.9%; 12 | 13 | --popover: 0 0% 100%; 14 | --popover-foreground: 240 10% 3.9%; 15 | 16 | --primary: 240 5.9% 10%; 17 | --primary-foreground: 0 0% 98%; 18 | 19 | --secondary: 240 4.8% 95.9%; 20 | --secondary-foreground: 240 5.9% 10%; 21 | 22 | --muted: 240 4.8% 95.9%; 23 | --muted-foreground: 240 3.8% 46.1%; 24 | 25 | --accent: 349 100% 47%; 26 | --accent-foreground: 240 5.9% 10%; 27 | 28 | --destructive: 0 84.2% 60.2%; 29 | --destructive-foreground: 0 0% 98%; 30 | 31 | --border: 240 5.9% 90%; 32 | --input: 240 5.9% 90%; 33 | --ring: 240 10% 3.9%; 34 | 35 | --radius: 0.5rem; 36 | } 37 | 38 | } 39 | 40 | @layer base { 41 | * { 42 | @apply border-border; 43 | } 44 | body { 45 | @apply bg-background text-foreground; 46 | } 47 | } 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/app/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codebucks27/AI-Powered-Twitter-Bio-Generator/d9967d8609c5b0d92f21bcc9973cb16a4a6d09ff/src/app/icon.png -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | import { GeistSans } from "geist/font/sans"; 3 | import { cn } from "@/lib/utils"; 4 | import { TooltipProvider } from "@/components/ui/tooltip"; 5 | import GridPattern from "@/components/magicui/grid-pattern"; 6 | 7 | export default function RootLayout({ 8 | children, 9 | }: Readonly<{ 10 | children: React.ReactNode; 11 | }>) { 12 | return ( 13 | 14 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Output from "@/components/home/Output"; 2 | import UserInput from "@/components/home/UserInput"; 3 | import AnimatedGradientText from "@/components/magicui/animated-gradient-text"; 4 | import { BioProvider } from "@/context/BioContext"; 5 | import { ChevronRight, Star } from "lucide-react"; 6 | import { Metadata } from "next"; 7 | import Link from "next/link"; 8 | 9 | export const metadata: Metadata = { 10 | title: "AI Twitter Bio Generator Built using Next.js", 11 | description: 12 | "Generate your perfect Twitter bio with the help of AI. Just answer a few questions and let our AI craft a bio that truly represents you.", 13 | }; 14 | 15 | export default function Home() { 16 | return ( 17 |
18 |
19 | 24 | 25 | 26 |
27 | Star on Github 28 | 29 |
30 | 31 |

32 | CRAFT THE PERFECT TWITTER BIO IN SECONDS! 33 |

34 |

35 | Just answer a few questions, and we'll generate a bio that captures 36 | who you are. 37 |

38 |
39 | 40 | 41 | 42 | 43 | 44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /src/components/home/CopyLabel.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Button } from "../ui/button"; 3 | 4 | const CopyLabel = ({ text }: { text: string }) => { 5 | const [label, setLabel] = useState("copy"); 6 | 7 | const copyToClipboard = async (text: string) => { 8 | try { 9 | await navigator.clipboard.writeText(text); 10 | } catch (err) { 11 | console.error("Failed to copy the text: ", err); 12 | } 13 | }; 14 | 15 | const handleClick = () => { 16 | copyToClipboard(text); 17 | setLabel("copied!"); 18 | }; 19 | 20 | return ( 21 | 28 | ); 29 | }; 30 | 31 | export default CopyLabel; 32 | -------------------------------------------------------------------------------- /src/components/home/Output.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useContext } from "react"; 3 | import { Badge } from "../ui/badge"; 4 | import { BorderBeam } from "../magicui/border-beam"; 5 | import { BioContext } from "@/context/BioContext"; 6 | import { Skeleton } from "../ui/skeleton"; 7 | import CopyLabel from "./CopyLabel"; 8 | 9 | const Output = () => { 10 | const { output, loading } = useContext(BioContext); 11 | 12 | return ( 13 |
14 | {loading && ( 15 | 21 | )} 22 | 23 | Output 24 | 25 | 26 | {loading ? ( 27 | 28 | ) : ( 29 |
    30 | {output?.data.map((data, index) => { 31 | return ( 32 |
  • 36 | {data.bio} 37 | 38 | 39 | 40 |
  • 41 | ); 42 | })} 43 |
44 | )} 45 |
46 | ); 47 | }; 48 | 49 | export default Output; 50 | -------------------------------------------------------------------------------- /src/components/home/UserInput.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useContext } from "react"; 3 | import { useForm } from "react-hook-form"; 4 | import { z } from "zod"; 5 | import { zodResolver } from "@hookform/resolvers/zod"; 6 | 7 | import { Button } from "@/components/ui/button"; 8 | import { 9 | Form, 10 | FormControl, 11 | FormField, 12 | FormItem, 13 | FormLabel, 14 | FormMessage, 15 | } from "@/components/ui/form"; 16 | import { 17 | Select, 18 | SelectContent, 19 | SelectItem, 20 | SelectTrigger, 21 | SelectValue, 22 | } from "@/components/ui/select"; 23 | import MetaIcon from "../icons/Meta"; 24 | import MistralIcon from "../icons/Mistral"; 25 | import { Slider } from "../ui/slider"; 26 | import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; 27 | import { Info, Loader2 } from "lucide-react"; 28 | import { Textarea } from "../ui/textarea"; 29 | import { Switch } from "../ui/switch"; 30 | import { generateBio } from "@/app/actions"; 31 | import { BioContext } from "@/context/BioContext"; 32 | 33 | const formSchema = z.object({ 34 | model: z.string().min(1, "Model is required!"), 35 | temperature: z 36 | .number() 37 | .min(0, "Temperature must be atleast 0") 38 | .max(2, "Temperature must be at most 1"), 39 | content: z 40 | .string() 41 | .min(50, "Content should atlest have 50 characters.") 42 | .max(500, "Content should not exceed 500 character limit."), 43 | type: z.enum(["personal", "brand"], { 44 | errorMap: () => ({ message: "Type is required!" }), 45 | }), 46 | tone: z.enum( 47 | [ 48 | "professional", 49 | "casual", 50 | "sarcastic", 51 | "funny", 52 | "passionate", 53 | "thoughtful", 54 | ], 55 | { 56 | errorMap: () => ({ message: "Tone is required!" }), 57 | } 58 | ), 59 | emojis: z.boolean(), 60 | }); 61 | 62 | const UserInput = () => { 63 | // 1. Define your form. 64 | const form = useForm>({ 65 | resolver: zodResolver(formSchema), 66 | defaultValues: { 67 | model: "llama3-8b-8192", 68 | temperature: 1, 69 | content: "", 70 | type: "personal", 71 | tone: "professional", 72 | emojis: false, 73 | }, 74 | }); 75 | 76 | const { setOutput, setLoading, loading } = useContext(BioContext); 77 | 78 | async function onSubmit(values: z.infer) { 79 | // Do something with the form values. 80 | // ✅ This will be type-safe and validated. 81 | // console.log(values); 82 | setLoading(true); 83 | 84 | const userInputValues = ` 85 | User Input: ${values.content}, 86 | Bio Tone: ${values.tone}, 87 | Bio Type: ${values.type}, 88 | Add Emojis: ${values.emojis} 89 | `; 90 | try { 91 | const { data } = await generateBio( 92 | userInputValues, 93 | values.temperature, 94 | values.model 95 | ); 96 | // console.log(data); 97 | setOutput(data); 98 | setLoading(false); 99 | } catch (e) { 100 | console.log(e); 101 | setLoading(false); 102 | } 103 | } 104 | 105 | return ( 106 |
107 |
108 | 112 |
113 | Settings 114 |
115 | ( 119 | 120 | Model 121 | 122 | 173 | 174 | 175 | 176 | )} 177 | /> 178 |
179 | 180 |
181 | ( 185 | 186 | 187 | 188 | Creativity 189 | 190 | 191 | 192 | 193 | 198 |

199 | A higher setting produces more creative and 200 | surprising bios, while a lower setting sticks to 201 | more predictable and conventional styles. 202 |

203 |
204 |
205 |
206 | {value} 207 |
208 | 209 | { 215 | onChange(val[0]); 216 | }} 217 | /> 218 | 219 | 220 |
221 | )} 222 | /> 223 |
224 |
225 | 226 |
227 | 228 | User Input 229 | 230 | 231 |
232 | ( 236 | 237 | 238 | About Yourself 239 | 240 | 241 |