Quick Links
Code: https://github.com/Lilypad-Tech/lilypad-front-end
What We'll Do
In this tutorial, we will review how to create a feeless front end for a Lilypad module. To do so, we will
Create a new Next JS project.
Create a CLI Wrapper to run the module.
Create a server to handle requests from the browser.
Create our user interface to allow a user to enter a prompt to run the module.
Run the front end
Install Next JS
Create a new Next JS project by running the following:
npx create-next-app@latest
Prepare your CLI Wrapper
In your project folder, create a new file named .env to manage your environment variables. Paste in the following, and replace “<your-private-key-here>” with the private key of the wallet that will be charged for all of the jobs.
WEB3_PRIVATE_KEY=<your-private-key-here>
To avoid publishing your private key online, add .env to your .gitignore file.
Install dotenv to access your environment variables.
npm install dotenv
In your app folder, create a folder for pages, then a folder for API, and in that folder, create a file named cliWrapper.js. Paste the following code into cliWrapper.js.
require('dotenv').config({ path: __dirname + '/../../../.env' });
const { exec } = require('child_process');
function runCliCommand(userInput, callback) {
console.log("Lilypad Starting...");
const web3PrivateKey = process.env.WEB3_PRIVATE_KEY;
if (!web3PrivateKey) {
console.error('WEB3_PRIVATE_KEY is not set in the environment variables.');
return;
}
const command = `export WEB3_PRIVATE_KEY=${web3PrivateKey} && lilypad run sdxl:v0.9-lilypad1 -i PromptEnv="PROMPT=${userInput}"`;
exec(command, async (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);
return callback(error);
}
if (stderr) {
console.error(`Stderr: ${stderr}`);
return callback(stderr);
}
const lines = stdout.trim().split('\n');
const path = lines[lines.length - 4].trim();
const filePath = path.replace('open ', '') + '/outputs/image-42.png';
console.log(stdout)
if (callback) {
callback(null, filePath);
}
});
}
module.exports = { runCliCommand };
Prepare your server
Install Express
npm install express
Install CORS
npm install cors
In your app folder, create a file named server.js and paste the following code:
const express = require('express');
const cors = require('cors');
const { runCliCommand } = require('./pages/api/cliWrapper');
const app = express();
const port = 3001;
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/images', express.static('/tmp/lilypad/data/downloaded-files'));
app.post('/api/cliWrapper', (req, res) => {
const userInput = req.body.userInput;
runCliCommand(userInput, (error, filePath) => {
if (error) {
return res.status(500).send('Error processing command');
}
const urlPath = filePath.replace('/tmp/lilypad/data/downloaded-files', '');
res.send(`http://localhost:${port}/images${urlPath}`);
});
});
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});
Prepare your front end
In the App folder, delete the default content on the Page.tsx file and paste in the following code:
"use client";
import React, { useState, FormEvent } from 'react';
import Image from 'next/image';
export default function Home(): JSX.Element {
const [userInput, setUserInput] = useState<string>('');
const [output, setOutput] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(false);
const handleSubmit = async (event: FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault();
setIsLoading(true);
try {
const response = await fetch('http://localhost:3001/api/cliWrapper', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ userInput }),
});
const imageURL = await response.text();
setOutput(imageURL);
} catch (error) {
console.error('Error:', error);
}
setIsLoading(false);
};
return (
<main>
<div className="flex min-h-screen flex-col items-center p-24">
<div className="z-10 max-w-5xl w-full flex flex-col items-center justify-center font-mono text-sm space-y-4">
<h1>Lilypad Image Generator</h1>
<div className="w-[500px] flex flex-col items-center"> {}
{}
{!output && (
<div className="border-dashed border-2 border-gray-300 w-full h-[500px] flex items-center justify-center">
{isLoading ? (
<span className="text-gray-500">Loading...</span> // Replace with your loading animation
) : (
<span className="text-gray-500">Image will appear here</span>
)}
</div>
)}
{}
{output && <Image src={output} alt="Generated" width={500} height={500} />}
<form onSubmit={handleSubmit} className="w-full mt-2">
{}
<textarea
className="w-full p-2"
value={userInput}
onChange={(e) => setUserInput(e.target.value)}
placeholder="Enter your prompt"
rows={4}
/>
<div className="w-full flex justify-end">
{}
<button type="submit" className="mt-4">Submit</button>
</div>
</form>
</div>
</div>
</div>
</main>
);
}
Running your front end
To run your front end on your local host, you will need to update the next.config.js file. Open the file and paste the following:
const nextConfig = {
images: {
domains: ['localhost'],
},
}
module.exports = nextConfig;
Now you're ready to test your front end!
Open two terminals, one to run your server and another to run your front end. In both terminals, navigate to your App folder. For your server terminal, run the following:
node server.js
You should see “Server listening at http://localhost:3001”.
In your second terminal, run your front end with the following command:
npm run dev
You should see something like this.

In your browser, navigate to http://localhost:3000/ and you should now be able to see your front end. It should look like this.

Type in a prompt and click on the enter button. If you look at the terminal running your server, you’ll notice that it says “Lilypad Starting…”. Lilypad will print to the console once the job is complete, and it should look something like this.

Now check your browser, and you should see your image.

Congrats! You have successfully integrated Lilypad into your front end.
Share your custom front ends with us!
If you were able to complete the tutorial, share your front end with us on Twitter!
If you need any help or have any feedback, hop into our Slack channel.
Want to learn more about Lilypad? Visit the docs here.