<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Danielle Hayes</title>
    <description>The latest articles on Forem by Danielle Hayes (@danielle_hayes_blogs).</description>
    <link>https://forem.com/danielle_hayes_blogs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3594796%2F0f436b1b-e35a-454d-8563-a193f6383413.png</url>
      <title>Forem: Danielle Hayes</title>
      <link>https://forem.com/danielle_hayes_blogs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danielle_hayes_blogs"/>
    <language>en</language>
    <item>
      <title>Secure Remote Access to AWS Environments Using VPN and Zero-Trust Principles</title>
      <dc:creator>Danielle Hayes</dc:creator>
      <pubDate>Tue, 04 Nov 2025 16:39:50 +0000</pubDate>
      <link>https://forem.com/danielle_hayes_blogs/secure-remote-access-to-aws-environments-using-vpn-and-zero-trust-principles-3m06</link>
      <guid>https://forem.com/danielle_hayes_blogs/secure-remote-access-to-aws-environments-using-vpn-and-zero-trust-principles-3m06</guid>
      <description>&lt;p&gt;Managing a cloud environment often means balancing security with accessibility. For teams working on AWS, providing remote access to resources while ensuring data safety is critical. In this post, I’ll walk through best practices for implementing secure remote access to your AWS environments using VPN and zero-trust principles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Secure Remote Access Matters
&lt;/h2&gt;

&lt;p&gt;Remote access is essential for developers, DevOps engineers, and IT admins. However, without proper controls, it can become a major attack vector. Common challenges include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Exposing sensitive data to public networks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unauthorized access from compromised endpoints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Difficulty in auditing and monitoring user activity&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A combination of VPNs and zero-trust access models can help mitigate these risks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up an AWS Client VPN
&lt;/h2&gt;

&lt;p&gt;AWS Client VPN is a managed VPN service that allows users to securely connect to your AWS VPC. Here’s a quick overview:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Navigate to the AWS VPC console and select Client VPN Endpoints.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a Client VPN endpoint, specifying your authentication method (Active Directory, mutual certificates, or SAML).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Associate the VPN with the appropriate VPC subnets to allow access to resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure authorization rules to control which users or groups can access which subnets.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Using mutual certificate authentication improves security compared to password-only methods.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 2: Implement Zero-Trust Access
&lt;/h2&gt;

&lt;p&gt;Zero-trust architecture ensures users are verified before granting access to any resource, even if they are inside the network. Key steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Enable AWS Verified Access or IAM policies that enforce least-privilege access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use multi-factor authentication (MFA) for all users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor access patterns via AWS CloudTrail and CloudWatch to detect anomalies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zero-trust principles minimize risk if a VPN credential or endpoint is compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Monitor and Audit Access
&lt;/h2&gt;

&lt;p&gt;Visibility is critical. For remote access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Enable VPC Flow Logs to track network activity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use AWS CloudTrail to log API calls and user actions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regularly review logs for suspicious behavior and enforce alerts for anomalies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This combination ensures you can detect unauthorized access attempts and respond quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Combine VPN and Zero-Trust for Best Security
&lt;/h2&gt;

&lt;p&gt;While VPNs provide a secure tunnel, they’re not enough alone. Integrating zero-trust access ensures that only authenticated and authorized users reach your AWS resources. Together, they provide:&lt;/p&gt;

&lt;p&gt;✅ Encrypted and secure remote connections&lt;br&gt;
✅ Controlled and auditable access to sensitive resources&lt;br&gt;
✅ Reduced attack surface even for remote users&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Secure remote access is crucial for teams working on AWS. By combining AWS Client VPN with zero-trust principles, you can provide your team with reliable access while protecting your cloud infrastructure.&lt;/p&gt;

&lt;p&gt;Have you implemented secure remote access in your AWS environment? Share your experiences or tips in the comments!&lt;/p&gt;

</description>
      <category>networking</category>
      <category>devops</category>
      <category>security</category>
      <category>aws</category>
    </item>
    <item>
      <title>Send Emails from Your Next.js App Using EmailJS</title>
      <dc:creator>Danielle Hayes</dc:creator>
      <pubDate>Tue, 04 Nov 2025 16:29:47 +0000</pubDate>
      <link>https://forem.com/danielle_hayes_blogs/send-emails-from-your-nextjs-app-using-emailjs-50kk</link>
      <guid>https://forem.com/danielle_hayes_blogs/send-emails-from-your-nextjs-app-using-emailjs-50kk</guid>
      <description>&lt;p&gt;Need a way to send emails directly from your frontend? Whether it’s contact forms, notifications, or feedback messages, EmailJS makes it simple — no backend required. Here’s a step-by-step guide to integrating EmailJS with your Next.js app.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is EmailJS?
&lt;/h2&gt;

&lt;p&gt;EmailJS allows you to send emails straight from JavaScript without needing a server. It supports popular email services like Gmail, Outlook, and more. This is perfect for sending transactional emails or form submissions directly from your frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an EmailJS Account
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Go to EmailJS and sign up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up an Email Service (e.g., Gmail).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a Template to define the email content (name, email, message, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy your Service ID, Template ID, and Public Key — you’ll need them later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install EmailJS SDK
&lt;/h2&gt;

&lt;p&gt;Install the official EmailJS SDK via npm or yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @emailjs/browser
# or
yarn add @emailjs/browser

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build a Contact Form
&lt;/h2&gt;

&lt;p&gt;Create a simple form in Next.js to capture user input and send emails via EmailJS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import { useState } from "react";
import emailjs from "@emailjs/browser";
import { toast } from "react-toastify";

export default function ContactForm() {
  const [formData, setFormData] = useState({ name: "", email: "", message: "" });

  const handleChange = (e) =&amp;gt; {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  const handleSubmit = async (e) =&amp;gt; {
    e.preventDefault();

    const serviceID = process.env.NEXT_PUBLIC_EMAILJS_SERVICE_ID;
    const templateID = process.env.NEXT_PUBLIC_EMAILJS_TEMPLATE_ID;
    const userID = process.env.NEXT_PUBLIC_EMAILJS_PUBLIC_KEY;

    try {
      const response = await emailjs.send(serviceID, templateID, formData, userID);
      if (response.status === 200) {
        toast.success("Message sent successfully!");
        setFormData({ name: "", email: "", message: "" });
      }
    } catch (error) {
      toast.error("Failed to send message. Please try again later.");
    }
  };

  return (
    &amp;lt;form onSubmit={handleSubmit}&amp;gt;
      &amp;lt;input
        type="text"
        name="name"
        placeholder="Your Name"
        value={formData.name}
        onChange={handleChange}
        required
      /&amp;gt;
      &amp;lt;input
        type="email"
        name="email"
        placeholder="Your Email"
        value={formData.email}
        onChange={handleChange}
        required
      /&amp;gt;
      &amp;lt;textarea
        name="message"
        placeholder="Your Message"
        value={formData.message}
        onChange={handleChange}
        required
      /&amp;gt;
      &amp;lt;button type="submit"&amp;gt;Send Message&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Highlights&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useState&lt;/code&gt;: Manages form data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;emailjs.send&lt;/code&gt;: Sends the email using EmailJS.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;toast&lt;/code&gt;: Shows success or error notifications (optional).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Use Environment Variables
&lt;/h2&gt;

&lt;p&gt;Keep sensitive data safe by storing your Service ID, Template ID, and Public Key in &lt;code&gt;.env.local&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_EMAILJS_SERVICE_ID=your_service_id
NEXT_PUBLIC_EMAILJS_TEMPLATE_ID=your_template_id
NEXT_PUBLIC_EMAILJS_PUBLIC_KEY=your_public_key

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Restart your development server after adding or updating environment variables.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deploy Your App
&lt;/h2&gt;

&lt;p&gt;Deploy your Next.js app to Vercel or any platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Push your code to GitHub.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect the repository in Vercel and deploy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the environment variables in Vercel dashboard to enable email functionality in production.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Why This Approach Works&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No backend needed: Send emails directly from your frontend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flexible: Works with any supported email service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quick setup: Perfect for contact forms, feedback forms, or notifications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this setup, your Next.js app can now send emails instantly and efficiently. 🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Get Telegram Notifications from Your Next.js App in Minutes</title>
      <dc:creator>Danielle Hayes</dc:creator>
      <pubDate>Tue, 04 Nov 2025 16:21:42 +0000</pubDate>
      <link>https://forem.com/danielle_hayes_blogs/get-telegram-notifications-from-your-nextjs-app-in-minutes-28ec</link>
      <guid>https://forem.com/danielle_hayes_blogs/get-telegram-notifications-from-your-nextjs-app-in-minutes-28ec</guid>
      <description>&lt;p&gt;Want to get real-time notifications from your website directly in Telegram? Whether it’s contact form submissions, system alerts, or user activity, using a Telegram bot is a simple and effective solution. Here’s how to set it up with Next.js.&lt;/p&gt;




&lt;h2&gt;
  
  
  Create a Telegram Bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open Telegram and search &lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Type /newbot and follow the instructions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save the Bot Token provided.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Get your Chat ID:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Personal chat: Send a message to your bot, then visit:&lt;br&gt;
&lt;code&gt;https://api.telegram.org/bot&amp;lt;YOUR_BOT_TOKEN&amp;gt;/getUpdates&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Group chat: Add the bot to a group, send a message, and use the same URL.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Set Up a Next.js API Route
&lt;/h2&gt;

&lt;p&gt;Create a server-side endpoint to send messages via the bot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// pages/api/notify.js
import axios from 'axios';

export default async function handler(req, res) {
  if (req.method !== 'POST') return res.status(405).json({ message: 'Method not allowed' });

  const { name, email, message } = req.body;
  const botToken = process.env.TELEGRAM_BOT_TOKEN;
  const chatId = process.env.TELEGRAM_CHAT_ID;

  try {
    const response = await axios.post(`https://api.telegram.org/bot${botToken}/sendMessage`, {
      chat_id: chatId,
      text: `New message:\nName: ${name}\nEmail: ${email}\nMessage: ${message}`,
    });
    res.status(200).json({ success: response.data.ok });
  } catch (err) {
    console.error(err);
    res.status(500).json({ success: false });
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Store &lt;code&gt;TELEGRAM_BOT_TOKEN&lt;/code&gt; and &lt;code&gt;TELEGRAM_CHAT_ID&lt;/code&gt; in &lt;code&gt;.env.local&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create a Contact Form
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import { useState } from 'react';
import axios from 'axios';

export default function ContactForm() {
  const [form, setForm] = useState({ name: '', email: '', message: '' });

  const handleChange = (e) =&amp;gt; setForm({ ...form, [e.target.name]: e.target.value });

  const handleSubmit = async (e) =&amp;gt; {
    e.preventDefault();
    try {
      const res = await axios.post('/api/notify', form);
      alert(res.data.success ? 'Message sent!' : 'Failed to send message.');
      if (res.data.success) setForm({ name: '', email: '', message: '' });
    } catch {
      alert('Error sending message.');
    }
  };

  return (
    &amp;lt;form onSubmit={handleSubmit}&amp;gt;
      &amp;lt;input name="name" value={form.name} onChange={handleChange} placeholder="Name" required /&amp;gt;
      &amp;lt;input name="email" value={form.email} onChange={handleChange} placeholder="Email" type="email" required /&amp;gt;
      &amp;lt;textarea name="message" value={form.message} onChange={handleChange} placeholder="Message" required /&amp;gt;
      &amp;lt;button type="submit"&amp;gt;Send&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy and Test
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add your environment variables in production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Submit the form — messages appear instantly in Telegram.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Why This Works&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Real-time updates: No need to constantly check your backend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lightweight: No complex notification system required.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flexible: Works for personal or group chats.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now your Next.js app can send instant notifications directly to Telegram, keeping you informed wherever you are. 🚀&lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Configuring Vite for Development Over VPN 🚀</title>
      <dc:creator>Danielle Hayes</dc:creator>
      <pubDate>Tue, 04 Nov 2025 16:10:34 +0000</pubDate>
      <link>https://forem.com/danielle_hayes_blogs/configuring-vite-for-development-over-vpn-47d8</link>
      <guid>https://forem.com/danielle_hayes_blogs/configuring-vite-for-development-over-vpn-47d8</guid>
      <description>&lt;p&gt;Vite is an amazing development tool with lightning-fast build times, hot-module reloading, and an intuitive configuration setup. However, working behind a VPN can sometimes cause issues when developing locally. Let’s walk through how to configure Vite to work seamlessly over a VPN.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Issue with VPNs?
&lt;/h2&gt;

&lt;p&gt;When you’re using a VPN, network requests and traffic are often tunneled through different gateways. This can affect how local development servers (like Vite) interact with your machine’s network interfaces. The most common issues are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Host Binding: Vite defaults to localhost (127.0.0.1), which might not be accessible over the VPN.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Port Conflicts: Certain VPNs block or reserve specific ports.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fixing Vite Configuration for VPNs
&lt;/h2&gt;

&lt;p&gt;To solve these issues, you need to tweak the Vite configuration slightly. Luckily, Vite offers an easy way to modify how the dev server behaves.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Bind Vite to Your Local Network Interface
&lt;/h3&gt;

&lt;p&gt;By default, Vite binds to localhost. You can configure Vite to bind to all network interfaces using the server.host option. This will allow devices connected to your local network (even over a VPN) to access your development server.&lt;/p&gt;

&lt;p&gt;Edit your &lt;code&gt;vite.config.js&lt;/code&gt; (or vite.config.ts if you're using TypeScript):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// vite.config.js
export default {
  server: {
    host: '0.0.0.0', // Bind to all available network interfaces
    port: 3000, // Default port, change if necessary
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Consider Changing the Port (if needed)
&lt;/h3&gt;

&lt;p&gt;Some VPNs block certain ports or cause conflicts with ports you’re trying to use. If you notice connection issues, try using a different port. You can easily change the port in your &lt;code&gt;vite.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// vite.config.js
export default {
  server: {
    host: '0.0.0.0',
    port: 8080, // Change to a different port, like 8080 or 5000
  },
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Use HTTPS if Required
&lt;/h3&gt;

&lt;p&gt;Some VPNs require secure communication over HTTPS. If that’s the case, you can enable HTTPS in Vite by generating SSL certificates or using self-signed ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// vite.config.js
import fs from 'fs';
import path from 'path';

export default {
  server: {
    host: '0.0.0.0',
    https: {
      key: fs.readFileSync(path.resolve(__dirname, 'path/to/your/ssl-key.pem')),
      cert: fs.readFileSync(path.resolve(__dirname, 'path/to/your/ssl-cert.pem')),
    },
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Tweak Your Firewall or VPN Configuration
&lt;/h3&gt;

&lt;p&gt;If you still face connectivity issues, ensure your VPN or firewall isn't blocking traffic on the port Vite uses. You might need to:&lt;/p&gt;

&lt;p&gt;Allow traffic to the development server port (e.g., 3000) in your VPN’s settings.&lt;br&gt;
Disable or adjust your firewall rules for local development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Working with Vite over a VPN doesn’t have to be a hassle. By binding Vite to all network interfaces, tweaking the port, and potentially adding HTTPS support, you can keep your development environment smooth—even when working remotely behind a VPN.&lt;/p&gt;

&lt;p&gt;Have any other tips or issues you encountered? Drop them in the comments below! 💬&lt;/p&gt;

&lt;p&gt;Happy coding! 🎉&lt;/p&gt;

</description>
      <category>networking</category>
      <category>tooling</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Applying an Image on a 3D Model with React Three Drei’s Decal</title>
      <dc:creator>Danielle Hayes</dc:creator>
      <pubDate>Tue, 04 Nov 2025 16:05:18 +0000</pubDate>
      <link>https://forem.com/danielle_hayes_blogs/applying-an-image-on-a-3d-model-with-react-three-dreis-decal-25am</link>
      <guid>https://forem.com/danielle_hayes_blogs/applying-an-image-on-a-3d-model-with-react-three-dreis-decal-25am</guid>
      <description>&lt;p&gt;Adding images or patterns to 3D models can bring your scene to life, especially if you’re building applications where customization, such as tattoo previews on mannequin models, is important. Here’s how you can achieve this using Decal from React Three Drei, a powerful set of helpers for React Three Fiber.&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;br&gt;
Before we start, make sure you have a project set up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;React Three Fiber for rendering 3D scenes with React&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;React Three Drei for additional 3D helpers like Decal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GLTF or OBJ model of a 3D object (e.g., mannequin or similar)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Install Required Packages
&lt;/h2&gt;

&lt;p&gt;If you haven’t yet, install @react-three/fiber, @react-three/drei, and three:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @react-three/fiber @react-three/drei three&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Set Up the 3D Scene and Canvas
&lt;/h2&gt;

&lt;p&gt;In your main App.js file, initialize the 3D scene. Here’s the basic setup for a Canvas component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// App.js
import React from 'react';
import { Canvas } from '@react-three/fiber';
import { Suspense } from 'react';
import { Loader } from '@react-three/drei';
import Experience from './components/Experience';

function App() {
  return (
    &amp;lt;div style={{ width: '100vw', height: '100vh' }}&amp;gt;
      &amp;lt;Canvas&amp;gt;
        &amp;lt;Suspense fallback={null}&amp;gt;
          &amp;lt;Experience /&amp;gt;
        &amp;lt;/Suspense&amp;gt;
      &amp;lt;/Canvas&amp;gt;
      &amp;lt;Loader /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the Canvas Component
&lt;/h2&gt;

&lt;p&gt;The Canvas component will load the model and apply a decal to it. Let’s break it down into steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Load the Model&lt;br&gt;
Use the useGLTF hook to load a 3D model. Make sure your model is placed in the public/models directory and is in .glb or .gltf format.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply the Decal Texture&lt;br&gt;
Use the Decal component from @react-three/drei. This component will allow you to apply an image to the model.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the Canvas Component
&lt;/h2&gt;

&lt;p&gt;Now let’s dive into the Canvas component code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Experience.jsx
import React, { useRef } from 'react';
import { useGLTF, Decal, useTexture } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';

function Experience() {
  // Load the 3D model
  const { nodes } = useGLTF('/models/male_mannequin.glb');

  // Load the texture to be applied as a decal
  const decalTexture = useTexture('/textures/tattoo.png');

  // A ref for rotating the model (optional)
  const modelRef = useRef();

  // Rotate the model for some interaction
  useFrame(() =&amp;gt; {
    modelRef.current.rotation.y += 0.01;
  });

  return (
    &amp;lt;group ref={modelRef} position={[0, -1.5, 0]}&amp;gt;
      {/* Mannequin model */}
      &amp;lt;mesh geometry={nodes.mannequin.geometry} castShadow receiveShadow&amp;gt;
        &amp;lt;meshStandardMaterial color="lightgrey" /&amp;gt;

        {/* Decal component to apply the image */}
        &amp;lt;Decal 
          position={[0, 0.5, 0.6]}     // Adjust position based on your model's needs
          rotation={[Math.PI, 0, 0]}    // Adjust rotation for orientation
          scale={0.3}                   // Adjust size of the decal
          map={decalTexture}            // Pass the loaded texture here
        /&amp;gt;
      &amp;lt;/mesh&amp;gt;
    &amp;lt;/group&amp;gt;
  );
}

export default Experience;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adjusting the Decal Properties
&lt;/h2&gt;

&lt;p&gt;To make sure the decal appears exactly where you want it, experiment with these properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;position: [x, y, z] coordinates to place the decal on the model. Adjust these based on the model’s geometry.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;rotation: [x, y, z] values to orient the decal. Use radians, e.g., Math.PI for 180 degrees.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;scale: Controls the size of the decal. Start small (e.g., 0.1 or 0.3) and increase as needed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding a Fallback or Error Boundary
&lt;/h2&gt;

&lt;p&gt;If the model or texture is having trouble loading, wrap the Experience component in a fallback or error boundary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Suspense fallback={&amp;lt;LoadingScreen /&amp;gt;}&amp;gt;
  &amp;lt;Experience /&amp;gt;
&amp;lt;/Suspense&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing and Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;Run the app, and you should see the decal applied to the 3D model! Adjust the position, rotation, and scale to fit the decal exactly where you want it.&lt;/p&gt;

&lt;p&gt;All set!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>react</category>
    </item>
  </channel>
</rss>
