React Integration
Learn how to integrate NestSMS into your React application for sending SMS messages through your backend API.
Setup Backend Proxy
First, create a backend API endpoint that will handle SMS requests:
Node.js/Express Backend
// server.js
const express = require("express");
const cors = require("cors");
require("dotenv").config();
const app = express();
app.use(cors());
app.use(express.json());
const NESTSMS_API_KEY = process.env.NESTSMS_API_KEY;
app.post("/api/sms/send", async (req, res) => {
try {
const { to, message } = req.body;
const response = await fetch("https://api.nestsms.com/api/v1/sms/send", {
method: "POST",
headers: {
"X-API-Key": NESTSMS_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
to,
message,
sender_id: "YourBrand",
message_type: "transactional",
}),
});
const data = await response.json();
res.json(data);
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.listen(3001, () => console.log("Backend running on port 3001"));React Components
SMS Form Component
// components/SMSForm.tsx
import { useState } from "react";
export function SMSForm() {
const [phone, setPhone] = useState("");
const [message, setMessage] = useState("");
const [loading, setLoading] = useState(false);
const [result, setResult] = useState<any>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setResult(null);
try {
const response = await fetch("http://localhost:3001/api/sms/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ to: phone, message }),
});
const data = await response.json();
setResult(data);
if (data.success) {
setPhone("");
setMessage("");
}
} catch (error) {
setResult({
success: false,
error: "Failed to send SMS. Please try again.",
});
} finally {
setLoading(false);
}
};
return (
<div className="mx-auto max-w-md p-6">
<h2 className="mb-4 text-2xl font-bold">Send SMS</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium">Phone Number</label>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="9840123456"
required
className="w-full rounded-lg border px-3 py-2"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium">Message</label>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Your message here..."
required
maxLength={720}
rows={4}
className="w-full rounded-lg border px-3 py-2"
/>
<p className="mt-1 text-sm text-gray-500">{message.length}/720 characters</p>
</div>
<button
type="submit"
disabled={loading}
className="bg-primary hover:bg-primary/80 w-full rounded-lg py-2 text-white disabled:bg-gray-400"
>
{loading ? "Sending..." : "Send SMS"}
</button>
</form>
{result && (
<div
className={`mt-4 rounded-lg p-4 ${
result.success ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"
}`}
>
{result.success ? (
<>
<p className="font-semibold">✓ SMS sent successfully!</p>
<p className="mt-1 text-sm">Job ID: {result.job_id}</p>
</>
) : (
<p>✗ Error: {result.error}</p>
)}
</div>
)}
</div>
);
}Custom Hook for SMS
// hooks/useSMS.ts
import { useState } from "react";
interface SendSMSParams {
to: string;
message: string;
}
interface SMSResult {
success: boolean;
job_id?: string;
error?: string;
}
export function useSMS() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const sendSMS = async ({ to, message }: SendSMSParams): Promise<SMSResult> => {
setLoading(true);
setError(null);
try {
const response = await fetch("http://localhost:3001/api/sms/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ to, message }),
});
const data = await response.json();
if (!data.success) {
setError(data.error || "Failed to send SMS");
}
return data;
} catch (err) {
const errorMessage = "Network error. Please try again.";
setError(errorMessage);
return { success: false, error: errorMessage };
} finally {
setLoading(false);
}
};
return { sendSMS, loading, error };
}Using the Custom Hook
// components/SMSFormWithHook.tsx
import { useState } from "react";
import { useSMS } from "../hooks/useSMS";
export function SMSFormWithHook() {
const [phone, setPhone] = useState("");
const [message, setMessage] = useState("");
const { sendSMS, loading, error } = useSMS();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const result = await sendSMS({ to: phone, message });
if (result.success) {
alert("SMS sent successfully!");
setPhone("");
setMessage("");
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="9840123456"
required
/>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Your message"
required
/>
<button type="submit" disabled={loading}>
{loading ? "Sending..." : "Send SMS"}
</button>
{error && <p className="error">{error}</p>}
</form>
);
}OTP Verification Flow
OTP Component
// components/OTPVerification.tsx
import { useState } from "react";
export function OTPVerification() {
const [phone, setPhone] = useState("");
const [otp, setOtp] = useState("");
const [step, setStep] = useState<"phone" | "verify">("phone");
const [loading, setLoading] = useState(false);
const sendOTP = async () => {
setLoading(true);
try {
const response = await fetch("http://localhost:3001/api/otp/send", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ phone }),
});
const data = await response.json();
if (data.success) {
setStep("verify");
}
} catch (error) {
alert("Failed to send OTP");
} finally {
setLoading(false);
}
};
const verifyOTP = async () => {
setLoading(true);
try {
const response = await fetch("http://localhost:3001/api/otp/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ phone, otp }),
});
const data = await response.json();
if (data.success) {
alert("OTP verified successfully!");
// Proceed with authentication
} else {
alert("Invalid OTP");
}
} catch (error) {
alert("Verification failed");
} finally {
setLoading(false);
}
};
if (step === "phone") {
return (
<div>
<h3>Enter Phone Number</h3>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="9840123456"
/>
<button onClick={sendOTP} disabled={loading}>
{loading ? "Sending..." : "Send OTP"}
</button>
</div>
);
}
return (
<div>
<h3>Enter OTP</h3>
<p>We sent a code to {phone}</p>
<input
type="text"
value={otp}
onChange={(e) => setOtp(e.target.value)}
placeholder="123456"
maxLength={6}
/>
<button onClick={verifyOTP} disabled={loading}>
{loading ? "Verifying..." : "Verify OTP"}
</button>
<button onClick={() => setStep("phone")}>Change Number</button>
</div>
);
}Using React Query
// hooks/useSMSQuery.ts
import { useMutation } from "@tanstack/react-query";
interface SendSMSParams {
to: string;
message: string;
}
export function useSendSMS() {
return useMutation({
mutationFn: async ({ to, message }: SendSMSParams) => {
const response = await fetch("http://localhost:3001/api/sms/send", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ to, message }),
});
if (!response.ok) {
throw new Error("Failed to send SMS");
}
return response.json();
},
});
}
// Usage in component
function SMSComponent() {
const sendSMS = useSendSMS();
const handleSend = () => {
sendSMS.mutate(
{ to: "9840123456", message: "Hello!" },
{
onSuccess: (data) => {
console.log("SMS sent:", data.job_id);
},
onError: (error) => {
console.error("Error:", error);
},
},
);
};
return (
<button onClick={handleSend} disabled={sendSMS.isPending}>
{sendSMS.isPending ? "Sending..." : "Send SMS"}
</button>
);
}Best Practices
- Never expose API keys in React code
- Always use a backend proxy for API calls
- Implement proper error handling
- Add loading states for better UX
- Validate phone numbers before sending
- Show clear success/error messages
- Add rate limiting on the backend