Full Guide on Send an Email with Attachment using Node.js and React

Full Guide on Send an Email with Attachment using Node.js and React

We need to install the Express framework, body-parser and other dependencies. It's a simple Node.js API powered by Express. After creating it we need to take the all benefit from Nodemailer and include the controller function that will send the submitted email with the image.

How to Send an Email with Image Attachment in Node.js and React

In the modern web, it becomes more and more popular having a contact form with an image attachment feature. You might ask why do I need it? Well, what if your customer found a bug and wants to send a screenshot to you? It makes sense right? There are many tutorials on the internet about sending emails with Node.js and Nodemailer but none of them covers the sending an attachment with it. We will go a step further and build a contact form with image attachment using Node.js Express with Nodemailer for back-end and React with Redux for Front-end. Let’s get started:

Setting up back-end

Firstly we need to set up our project:

npm init

So I assume you are already familiar with generating projects with Node.js and React, so will not go further for basic topics. We need to install the Express framework, body-parser and other dependencies.

npm install express body-parser nodemailer cors

Let’s create our main server file called index.js:

const express = require('express');
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
const cors = require('cors');
const app = express();
const port = 4444;
app.use(bodyParser.json({ limit: '10mb', extended: true }))
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }))
app.use(cors());
app.listen(port, () => {
console.log('We are live on port 4444');
});
app.get('/', (req, res) => {
res.send('Welcome to my api');
})

It’s a simple Node.js API powered by Express. After creating it we need to take the all benefit from Nodemailer and include the controller function that will send the submitted email with the image.

Creating contact controller function

We are going to use Gmail for authentication. However, you can use Sendgrid’s API which offers a better and more secure option. We are creating it in the Index.js since there is no any other functionality. We need just this simple controller and API route for sending post requests.

app.post('/api/v1/contact', (req, res) => {
var data = req.body;
var smtpTransport = nodemailer.createTransport({
service: 'Gmail',
port: 465,
auth: {
user: 'username',
pass: 'password'
}
});
var mailOptions = {
from: data.email,
replyto: data.email,
to: '[email protected]',
subject: data.title,
html: `<p>${data.email}</p>
<p>${data.message}</p>`,
attachments: [
{
filename: data.title + ".jpg",
contentType:  'image/jpeg',
content: new Buffer.from(req.body.image.split("base64,")[1], "base64"),
}
]
};
smtpTransport.sendMail(mailOptions,
(error, response) => {
if (error) {
res.status(400).send(error)
} else {
res.send('Success')
}
smtpTransport.close();
});
})

Inside the controller function, you will see the attachment field with supported file types. This is a built-in feature that comes with Nodemailer.

It’s time to move on Front-end.

Creating React.js project

We are going to use Create React App for generating our project.

npx create-react-app Contact

I guess you are familiar with react and have some knowledge so will not go further for explaining project structure. While this tutorial covers a small part for a real-world project we are going to create reusable components because it’s predictable that you are going to use these reusable components not only for contact form but also for other forms in the project like sign-in sign-up. posting, commenting and so on…

Creating reusable form components

Firstly we will start with simple input component:

import React from ‘react’;

export const ProjectInput = ({
 input,
 label,
 type,
 symbol,
 className,
 meta: { touched, error, warning }
}) => (
 <div className=’form-group’>
 <label>{label}</label>
 <div className=’input-group’>
 {symbol &&
 <div className=’input-group-prepend’>
 <div className=’input-group-text’>{symbol}</div>
 </div>
 }
 <input {…input} type={type} className={className} />
 </div>
 {touched &&
 ((error && <div className=’alert alert-danger’>{error}</div>))}
 </div>
 )

Then will create TextArea

import React from ‘react’;

export const ProjectTextArea = ({
 input,
 label,
 type,
 rows,
 className,
 meta: { touched, error, warning }
}) => (
 <div className=’form-group’>
 <label>{label}</label>
 <div className=’input-group’>
 <textarea {…input} type={type} rows={rows} className={className}></textarea>
 </div>
 {touched &&
 ((error && <div className=’alert alert-danger’>{error}</div>))}
 </div>
 )

It’s time to create the most important reusable component of this form, an input for uploading the image.

Creating ImgFileUpload component

We are going to use FileReader. It is an object with the sole purpose of reading data from Blob (and hence File too) objects.

It delivers the data using events, as reading from disk may take time.

import React from ‘react’;

export class ImgFileUpload extends React.Component {

constructor() {
 super();
 
this.setupReader()
this.state = {
 selectedFile: undefined,
 imageBase64: ‘’,
 initialImageBase64: ‘’,
 pending: false,
 status: ‘INIT’,
 }
 
this.onChange = this.onChange.bind(this);
 }
 
setupReader() {
 this.reader = new FileReader();
 this.reader.addEventListener(‘load’, (event) => {
 const { initialImageBase64 } = this.state;
 var { changedImage } = this.props;
 const imageBase64 = event.target.result;
 changedImage(imageBase64);
 
if (initialImageBase64) {
 this.setState({ imageBase64 });
 } else {
 this.setState({ imageBase64, initialImageBase64: imageBase64 });
 }
 });
 }
 
onChange(event) {
 const selectedFile = event.target.files[0];
 var { checkImageState } = this.props;
 if (selectedFile) {
 checkImageState(‘selected’);
 } else {
 checkImageState(‘unselected’);
 }
 if (selectedFile) {
 this.setState({
 selectedFile,
 initialImageBase64: ‘’
 });
 
this.reader.readAsDataURL(selectedFile);
 }
 }
 
render() {

return (
 <div className=’img-upload-container’>
 <label className=’img-upload btn’>
 <span className=’upload-text’> Select an image </span>
 <input type=’file’
 accept=’.jpg, .png, .jpeg’
 onChange={this.onChange} />
 </label>
 </div>
 
)
 }
}

There are many 3rd party libraries that you might find for file selection and uploading, however, it’s recommended to ignore those libraries. Some of them cause different issues from which the most popular is that you can’t create a production build because of a single 3rd party library. We got a similar issue when used 3rd party package for Angular in the past. So as fewer libraries you use as fewer issues will be in the future.

Building a complete contact form

We need to put all the above-created components in one place for getting our final form but before that, we need to install some dependencies.

npm install --save redux react-redux redux-form axios

then we need to create an index.js file that will contain all our reducers as well as our formReducer

import { createStore, compose, combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
export const init = () => {
const reducer = combineReducers({
form: formReducer,
});
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer);
return store;
}

Then don’t forget to implement our store inside App.js:

import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import Contact from './components/contact';
import './App.css';
const store = require('./reducers').init();
class App extends Component {
render() {
return (
<Provider store={store}>
<BrowserRouter>
<div className='App'>
<div className='container'>
<Route exact path='/' component={Contact} />
</div>
</div>
</BrowserRouter>
</Provider>
);
}
}
export default App;

Now, we can continue building our final contact form.

import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { ProjectInput } from './ProjectInput';
import { ProjectTextArea } from './ProjectTextArea';
import { ImgFileUpload } from './ImgFileUpload';
class ContactForm extends React.Component {
state = {
imageState: false
}
render() {
const {
handleSubmit,
pristine,
submitting,
submitCb,
valid,
SetImage,
loading
} = this.props;
return (
<form onSubmit={handleSubmit(submitCb).bind(this)}
onClick={this.resetValues}>
<Field
name="email"
type="email"
label='Email'
className='form-control'
component={ProjectInput}
/>
<Field
name="title"
type="text"
label='Title'
className='form-control'
component={ProjectInput}
/>
<Field
name="message"
type="text"
label='Description'
rows='6'
className='form-control'
component={ProjectTextArea}
/>
<Field
name="image"
label='Image'
className='form-control'
component={ImgFileUpload}
props={{
changedImage: (e) => {
SetImage(e);
this.setState({
imageState: true
})
},
checkImageState: (e) => {
if (e === 'selected') {
this.setState({
imageState: true
});
} else {
this.setState({
imageState: false
});
}
},
}}
key={this.props.key}
/>
{
loading ?
<button
className='btn btn-primary'
type="button"
disabled={true}
>
Sending...
</button>
:
<button
className='btn btn-primary'
type="submit"
disabled={!valid || pristine || submitting || !this.state.imageState}
>
Send
</button>
}
</form>
)
}
}
const validate = values => {
const errors = {};
if (!values.email) {
errors.email = 'Please enter email!';
}
if (!values.title) {
errors.title = 'Please enter title!';
}
if (!values.message) {
errors.message = 'Please enter message!';
}
return errors;
}
export default reduxForm({
form: 'ContactForm',
validate
})(ContactForm)

We used reusable components which created earlier and used redux-form to power our form. Also, we implemented some validations for this form however, since it’s a completely different topic, I will recommend adding even more validations for each field separately. Besides that, we disabled the send button until all fields will be filled. Looks lots of work done right? As the final step for making our contact form completely functional, we are going to create a container component for it.

import React from 'react';
import ContactForm from './ContactForm';
import { reset } from 'redux-form';
import axios from 'axios';
import { connect } from "react-redux";
import { ToastContainer, toast } from 'react-toastify';
class Contact extends React.Component {
constructor(props) {
super(props);
this.state = {
errors: [],
note: '',
loading: false
}
this.pristine = false;
this.Send = this.Send.bind(this);
}
SetImage = async (image) => {
await this.setState({ image });
};
Send(userData) {
let { image } = this.state;
userData = { ...userData, image };
this.setState({ loading: true });
this.sendEmail(userData).then(
submited => {
toast.success('Email sent successfully');
this.props.dispatch(reset('ContactForm'));
this.setState({ key: 'cleared' })
this.setState({ note: 'Email sent successfully', loading: false });
},
).catch(errors => {
toast.error('Error occured')
this.setState({ errors, loading: false })
});
};
sendEmail = async emailData => {
console.log(emailData);
return axios.post('/api/v1/contact', emailData).then(
res => res.data,
err => Promise.reject(err.response.data.errors)
)
};
render() {
const { errors } = this.state;
return (
<section id='contact'>
<ToastContainer />
<div className='bwm-form'>
<div className='row'>
<div className='col-md-5'>
<h1>Contact Us</h1>
<ContactForm
loading={this.state.loading}
submitCb={this.Send}
errors={errors}
SetImage={this.SetImage}
pristine={this.pristine}
key={this.state.key}
/>
</div>
</div>
</div>
</section>
)
}
}
export default connect(null, null)(Contact);

In the above component, we send data to the back-end. If the email was sent successfully the form is getting reset automatically. Also, we used a toaster library for showing an alert however you can just ignore that library. As you see we used Axios for doing a post request and sending data to the back-end. As far as Redux-form supports only resetting basic fields we created the functionality of resetting file input from scratch by using a special key in the contact form and contact components.

That’s it! Hope this tutorial was helpful.
You can download the full source code here: Github

Thank you for reading! Please share if you liked it!

Hire React Js Developer from Expert React JS Development Company

Hire React Js Developer from Expert React JS Development Company

Are you looking to [hire React JS developer](https://www.mobiwebtech.com/react-js-development-company/ "hire React JS developer") from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team...

Are you looking to hire React JS developer from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team of expert React JS developers, you can hire them on an hourly, weekly or monthly basis for the successful completion of your dream project.

Learn React.js: Front-End Web Development for Beginners

Learn React.js: Front-End Web Development for Beginners

Learn React.js - Frontend Web Development for Beginners. In this React tutorial, you will learn the core fundamentals of React JS and Modern JavaScript so that you can start building lightning fast web apps using React JS. Learn Modern JavaScript and React JS from absolute scratch. Learn to make AJAX requests to get data from remote API and display into your web application. Learn React JS, which is one of the most exciting technology of recent time

Learn React.js - Frontend Web Development for Beginners - Web Development in 2019. In this video you will learn the core fundamentals of React JS and Modern JavaScript so that you can start building lightning fast web apps using React JS.

In this video, you will learn Modern JavaScript and React JS from absolute scratch. you will learn to make AJAX requests to get data from remote API and display into your web application.

So if you are ready to learn React JS, which is one of the most exciting technology of recent time

Thanks for watching

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

React - The Complete Guide (incl Hooks, React Router, Redux)

**☞ **Modern React with Redux [2019 Update]

**☞ **50+ Java Interview Questions for Programmers

**☞ **Top 100 Python Interview Questions and Answers

**☞ **100+ Basic Machine Learning Interview Questions and Answers

**☞ **Introduction to Java String Interview Questions and Answers

**☞ **Best 50 React Interview Questions and Answers in 2019

**☞ **Top 50+ SQL Interview Questions and Answers in 2019

**☞ **Best Java Microservices Interview Questions In 2019

**☞ **Best 50 Nodejs interview questions from Beginners to Advanced in 2019

**☞ **100+ Java Interview Questions and Answers In 2019

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

Hire Now: https://bit.ly/394wdOx