Learn how to build engaging newsletter subscriptions with Lovable. Discover proven tactics to captivate your audience and expand your mailing list effortlessly.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Setting Up Your Lovable Project Environment
Adding Dependencies Directly in Code
index.html
), add the following script tag inside the <head>
section. This example uses Axios to send requests to our backend:
<head>
<meta charset="UTF-8">
<title>Newsletter Subscription</title>
<!-- Include Axios from CDN for AJAX requests -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
Creating the Newsletter Subscription Form
index.html
file in your Lovable project.<body>
tag to create a simple subscription form:
<body>
<h1>Subscribe to Our Newsletter</h1>
<form id="subscription-form">
<input type="email" id="email" name="email" placeholder="Enter your email address" required>
<button type="submit">Subscribe</button>
</form>
<div id="message"></div>
<script src="subscribe.js"></script>
</body>
subscribe.js
file (created in the next step) will handle the submission.
Implementing the Subscription Form Logic
subscribe.js
in your Lovable project’s file panel.subscribe.js
. This code captures the form submission, sends the email data to the backend endpoint using Axios, and displays a success or error message:
document.getElementById('subscription-form').addEventListener('submit', function(e) {
e.preventDefault();
var email = document.getElementById('email').value;
// Send the subscription data to the backend via POST request
axios.post('/api/subscribe', { email: email })
.then(function(response) {
document.getElementById('message').innerText = 'Thank you for subscribing!';
})
.catch(function(error) {
document.getElementById('message').innerText = 'Subscription failed. Please try again later.';
});
});
Creating the Backend Subscription Endpoint
subscribeFunction.js
.subscribeFunction.js
to handle incoming POST requests. This function simulates saving the email subscription (in a real-world scenario, you might connect to a database or an external email service):
exports.handler = async function(request, response) {
// Parse the request body (assumes JSON format)
try {
const data = JSON.parse(request.body);
const email = data.email;
// Simulate saving the email address (replace this with database logic)
console.log('New subscription: ' + email);
return response.status(200).json({ message: 'Subscription successful' });
}
catch (error) {
return response.status(400).json({ message: 'Invalid request' });
}
};
/api/subscribe
to this serverless function. Use the Lovable dashboard configuration to map the endpoint accordingly.
Connecting the Frontend to the Backend
/api/subscribe
is handled by the subscribeFunction.js
function./api/subscribe
and assign it to your newly created function.
Testing the Newsletter Subscription Feature
Final Adjustments and Deployment
subscribe.js
).styles.css
file if you want to adjust the appearance of your form.
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
router.use(bodyParser.json());
router.post('/api/subscribe', async (req, res) => {
try {
const { email, name, interests } = req.body;
if (!email || !/^[^\s@]+@[^\s@]+.[^\s@]+$/.test(email)) {
return res.status(400).json({ error: 'Invalid email address.' });
}
const subscription = {
email,
name: name || 'Valued Subscriber',
interests: Array.isArray(interests) && interests.length ? interests : ['general'],
subscribedAt: new Date().toISOString(),
status: 'pending'
};
// Simulate storing the subscription in the database
const savedSubscription = await saveSubscription(subscription);
// Simulate sending a confirmation email via Lovable's API integration
await sendLovableConfirmation(savedSubscription.email, savedSubscription.id);
res.status(201).json({
message: 'Subscription initiated. Please check your email to confirm.',
subscriptionId: savedSubscription.id
});
} catch (error) {
res.status(500).json({ error: 'Internal server error.' });
}
});
function saveSubscription(data) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: 'sub\_' + Date.now(), ...data, status: 'active' });
}, 500);
});
}
function sendLovableConfirmation(email, subscriptionId) {
return new Promise((resolve) => {
console.log(`Sending confirmation email to ${email} for subscription ${subscriptionId}`);
setTimeout(() => resolve(true), 300);
});
}
module.exports = router;
const express = require('express');
const crypto = require('crypto');
const router = express.Router();
const bodyParser = require('body-parser');
router.use(bodyParser.json({
verify: (req, res, buf, encoding) => {
req.rawBody = buf.toString(encoding || 'utf8');
}
}));
const LOVABLE_WEBHOOK_SECRET = process.env.LOVABLE_WEBHOOK_SECRET || 'your_webhook_secret\_here';
async function updateSubscriptionRecord(subscriptionId, newStatus) {
return new Promise(resolve => {
setTimeout(() => {
resolve({ id: subscriptionId, status: newStatus });
}, 300);
});
}
router.post('/api/lovable-webhook', async (req, res) => {
try {
const providedSignature = req.headers['x-lovable-signature'];
if (!providedSignature) {
return res.status(400).json({ error: 'Missing signature header.' });
}
const expectedSignature = crypto
.createHmac('sha256', LOVABLE_WEBHOOK_SECRET)
.update(req.rawBody)
.digest('hex');
if (providedSignature !== expectedSignature) {
return res.status(401).json({ error: 'Signature verification failed.' });
}
const { subscriptionId, eventType, timestamp } = req.body;
if (!subscriptionId || !eventType) {
return res.status(400).json({ error: 'Invalid webhook payload.' });
}
let statusUpdate = 'active';
switch (eventType) {
case 'subscription\_confirmed':
statusUpdate = 'active';
break;
case 'subscription\_canceled':
statusUpdate = 'canceled';
break;
case 'subscription\_error':
statusUpdate = 'error';
break;
default:
statusUpdate = 'pending';
}
const updatedSubscription = await updateSubscriptionRecord(subscriptionId, statusUpdate);
res.status(200).json({
message: 'Webhook processed successfully.',
subscription: updatedSubscription,
receivedAt: timestamp || new Date().toISOString()
});
} catch (err) {
res.status(500).json({ error: 'Internal server error.' });
}
});
module.exports = router;
const express = require('express');
const axios = require('axios');
const router = express.Router();
const bodyParser = require('body-parser');
router.use(bodyParser.json());
router.post('/api/update-preferences', async (req, res) => {
try {
const { subscriptionId, preferencesUpdate } = req.body;
if (!subscriptionId || !preferencesUpdate || typeof preferencesUpdate !== 'object') {
return res.status(400).json({ error: 'Invalid request payload.' });
}
// Simulate retrieving the current subscription data from the database
const currentSubscription = await fetchSubscription(subscriptionId);
if (!currentSubscription) {
return res.status(404).json({ error: 'Subscription not found.' });
}
const updatedPreferences = { ...currentSubscription.preferences, ...preferencesUpdate };
// Update preferences in Lovable's system via API call
const lovableResponse = await axios.post('https://api.lovable.io/v1/subscriptions/update', {
subscriptionId,
preferences: updatedPreferences
}, {
headers: { 'Authorization': `Bearer ${process.env.LOVABLE_API_TOKEN}` }
});
if (!lovableResponse.data || !lovableResponse.data.success) {
return res.status(502).json({ error: 'Failed to update preferences on Lovable.' });
}
// Simulate updating the subscription record in the local database
const updatedSubscription = await updateLocalSubscription(subscriptionId, updatedPreferences);
res.status(200).json({
message: 'Subscription preferences updated successfully.',
subscription: updatedSubscription
});
} catch (error) {
res.status(500).json({ error: 'Internal server error.' });
}
});
function fetchSubscription(id) {
return new Promise(resolve => {
setTimeout(() => {
// Simulated subscription record
resolve({ id, preferences: { frequency: 'weekly', topics: ['news', 'tech'] } });
}, 300);
});
}
function updateLocalSubscription(id, preferences) {
return new Promise(resolve => {
setTimeout(() => {
// Simulate saving updated preferences to local database
resolve({ id, preferences });
}, 300);
});
}
module.exports = router;
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Understanding Your Newsletter Objectives
Choosing the Right Tools and Platforms
Integrating AI Code Generators
Building a Subscription Form Code
Example Code for a Simple Newsletter Subscription Form
Subscribe to Our Newsletter