Hey there ๐ , I hope you're doing great. This is just a quick guide on how you can build amazing applications using AI.
Inspiration for me to build a recipe generator comes from wanting to solve the mental problem of deciding what to eat. you know they say making too many small decisions causes you to suffer fromdecisions fatigue - which is basically mental exhaustion resulting from the sheer number of decisions a person must make daily, leading to difficulty makingโor making goodโdecisions. Now just imagine an application in which you just need to key in the ingredients, click the generate button then boom ๐ฅ there you have generated the recipe. That is what we are going to build step by step. Check below what we will be building ๐๐
Without any further ado let's dive in
Step1. Create new project
First, ensure you have Node.js installed, then run the following command on the terminal to initialise a Nextjs project.
npx create-next-app recipe-generator
cd recipe-generator
Step2. Install required packages
Run the following to install packages we will be using
npm install langchain
npm install openai dotenv
Let's get to understand the two,
LangChain LangChain is a tool that helps create applications using large language models (LLMs). It provides a framework to connect these models with various data sources, such as APIs and databases, making it easier to build more advanced AI applications like chatbots, content generators, and data analyzers. Essentially, LangChain streamlines the process of integrating and using AI models in different projects.
OpenAI is the organization behind some of the most advanced AI models, like GPT, which can generate text, answer questions, and perform a variety of other tasks. The OpenAI API allows developers to access these powerful models and use them in their own applications. While OpenAI provides the AI models, LangChain acts as the framework that can connect these models with other tools and data sources, enabling the creation of complex AI-driven applications.
Together, OpenAI and LangChain can be used to build sophisticated AI systems. OpenAI offers the intelligence, and LangChain provides the tools to integrate that intelligence into useful, real-world applications.
Step3. Configure Environment variables
Create a .env.local
file in the root directory. Go to openai and create a new project, click on the dashboard and select API keys from the left sidebar to copy your openai-api-key.
Insert your api key in the .env.local file as shown below
OPENAI_API_KEY=your opeai-api-key
Step4. Setup the API connection
In the app directory create langchain.ts
file and in it insert the following code
// aap/langchain.ts
import { OpenAI } from '@langchain/openai'
import dotenv from 'dotenv';
dotenv.config();
const model = new OpenAI({
openAIApiKey: process.env.NEXT_PUBLIC_OPENAI_API_KEY,
temperature: 0.7, // Adjust for creativity vs. accuracy
});
export default model;
Here we set up an OpenAI model using the LangChain library, configuring it with an API key and creativity level from environment variables. We then export this model so it can be used throughout the web application.
Step5. Creating recipe generator logic
In the app directory create a new file recipeGenerator.ts
and copy and paste the following code into it.
// app/recipeGenerator.ts
import model from './langchain';
export async function generateRecipe(ingredients: string[]): Promise<string> {
const prompt = `Generate a recipe using the following ingredients: ${ingredients.join(', ')}. Please include the steps for preparation.`;
const response = await model.call(prompt);
return response;
}
Here we are defining a function generateRecipe that takes a list of ingredients as input and uses the OpenAI model (imported from langchain.ts) to create a recipe. The function constructs a prompt with the ingredients, sends it to the model to generate a recipe, and then returns the recipe text.
Step6. Create frontend to use the API
Insert the following code into the page.tsx file.
// app/page.tsx
"use client"; // Ensure this is a client component
import { useState } from 'react';
import { generateRecipe } from './recipeGenerator'; // Adjust path if needed
const Home = () => {
const [ingredients, setIngredients] = useState<string[]>([]);
const [recipe, setRecipe] = useState<string>('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setError(null);
try {
const newRecipe = await generateRecipe(ingredients);
setRecipe(newRecipe);
} catch (err) {
console.error("Error generating recipe:", err);
setError('Failed to generate recipe. Please try again.');
} finally {
setIsLoading(false);
}
};
const handleIngredientsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newIngredients = e.target.value
.split(',')
.map(i => i.trim())
.filter(i => i.length > 0);
setIngredients(newIngredients);
};
return (
<div style={{ padding: '20px', border: '2px solid blue' }}> {/* Debugging styles */}
<h1>AI Recipe Generator</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
value={ingredients.join(', ')}
onChange={handleIngredientsChange}
placeholder="Enter ingredients separated by commas"
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Generating...' : 'Generate Recipe'}
</button>
</form>
{error && <p style={{ color: 'red' }}>{error}</p>}
{recipe && (
<div>
<h2>Generated Recipe:</h2>
<p>{recipe}</p>
</div>
)}
</div>
);
};
export default Home;
Here we are creating a React component for an AI recipe generator. Users input ingredients into a text field, which is then processed to generate a recipe using the generateRecipe function. The component handles form submission, displays loading states, and shows any errors or the generated recipe.
Step7. Styling our component
To style our application, insert the following CSS code in the global.css file
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 50, 50, 50; /* Slightly lighter dark grey for text */
--background-start-rgb: 255, 248, 240; /* Warm beige for start */
--background-end-rgb: 255, 255, 255; /* Soft white for end */
--highlight-color: 255, 140, 0; /* Orange for highlights and accents */
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 240, 240, 240; /* Light grey text for dark mode */
--background-start-rgb: 34, 34, 34; /* Dark grey for start */
--background-end-rgb: 0, 0, 0; /* Black for end */
--highlight-color: 255, 165, 0; /* Lighter orange for highlights in dark mode */
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
rgb(var(--background-start-rgb)),
rgb(var(--background-end-rgb))
);
font-family: 'Poppins', sans-serif;
line-height: 1.7;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
}
h1, h2, h3, h4, h5, h6 {
color: rgb(var(--highlight-color)); /* Warm orange color for headings */
font-family: 'Merriweather', serif; /* Add a serif font for headings */
text-align: center;
margin-bottom: 20px;
}
.container {
width: 90%;
max-width: 600px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
padding: 20px;
border-radius: 8px;
background-color: #fff;
margin-bottom: 20px;
}
button {
background-color: rgb(var(--highlight-color));
color: #fff;
border: none;
border-radius: 8px;
padding: 12px 24px;
font-size: 18px;
font-family: 'Poppins', sans-serif;
cursor: pointer;
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s ease, transform 0.3s ease;
width: 100%;
margin-top: 10px;
}
button:hover {
background-color: #e68a00; /* Darker orange on hover */
transform: translateY(-2px); /* Slight lift effect */
}
input {
border: 2px solid rgb(var(--highlight-color));
border-radius: 8px;
padding: 12px;
font-size: 16px;
margin-bottom: 12px;
width: 100%;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
input:focus {
outline: none;
border-color: #e68a00; /* Darker orange on focus */
box-shadow: 0 0 5px rgba(255, 140, 0, 0.5);
}
/* Remove debugging styles */
* {
border: none;
}
Finally, we've set up a layout.tsx file to define the overall structure of our AI Recipe Generator application. This file applies global styles, sets metadata (like the page title and description), and organizes the main layout by wrapping all content (children) in a consistent header, main section, and footer. The debugging border helps visualize the main content area during development.
// app/layout.tsx
import './globals.css';
export const metadata = {
title: 'AI Recipe Generator',
description: 'Generate recipes using AI',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<header>
{/* Header content */}
</header>
<main>
<div style={{ border: '1px solid red' }}> {/* Added for debugging */}
{children}
</div>
</main>
<footer>
{/* Footer content */}
</footer>
</body>
</html>
);
}
Run the application and see it in action
npm run dev
That's it guys, if you liked this tutorial kindly share it with others and make sure you follow me for much more ๐ฅ. let me also know if you need the source code.
Follow me on X
Follow me on LinkedIn
Top comments (0)