The Palos Publishing Company

Follow Us On The X Platform @PalosPublishing
Categories We Write About

Build a lightweight CRM

To build a lightweight CRM (Customer Relationship Management) system, you’ll want a simple yet functional tool that helps manage contacts, interactions, sales pipelines, and notes without the complexity or overhead of enterprise-level systems. Below is a step-by-step guide to building a basic CRM using modern web technologies.


Tech Stack Recommendation

  • Frontend: React + Tailwind CSS

  • Backend: Node.js + Express

  • Database: MongoDB (or SQLite for simplicity)

  • Authentication: JWT (JSON Web Token)

  • Optional Tools: Vite (for fast React dev), Mongoose (if using MongoDB)


Core Features

  1. User Authentication

  2. Contact Management

  3. Notes/Interactions Log

  4. Sales Pipeline (Leads, Opportunities, Closed Deals)

  5. Simple Dashboard


Step-by-Step Implementation

1. Setup the Backend (Node.js + Express)

Project Structure:

bash
/crm-backend /controllers /models /routes server.js

server.js

javascript
const express = require('express'); const mongoose = require('mongoose'); const cors = require('cors'); const app = express(); app.use(cors()); app.use(express.json()); mongoose.connect('mongodb://localhost/crm', { useNewUrlParser: true, useUnifiedTopology: true }); app.use('/api/users', require('./routes/userRoutes')); app.use('/api/contacts', require('./routes/contactRoutes')); app.listen(5000, () => console.log('Server running on port 5000'));

2. Define Models (e.g., Mongoose)

User Model

javascript
const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ username: String, password: String, }); module.exports = mongoose.model('User', userSchema);

Contact Model

javascript
const contactSchema = new mongoose.Schema({ name: String, email: String, phone: String, company: String, status: { type: String, enum: ['Lead', 'Opportunity', 'Customer'] }, owner: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } }); module.exports = mongoose.model('Contact', contactSchema);

3. API Routes

User Authentication (userRoutes.js)

javascript
const express = require('express'); const router = express.Router(); const jwt = require('jsonwebtoken'); const User = require('../models/User'); router.post('/login', async (req, res) => { const { username, password } = req.body; const user = await User.findOne({ username, password }); if (!user) return res.status(401).send('Invalid credentials'); const token = jwt.sign({ id: user._id }, 'secret'); res.json({ token }); });

Contacts (contactRoutes.js)

javascript
const express = require('express'); const router = express.Router(); const Contact = require('../models/Contact'); router.get('/', async (req, res) => { const contacts = await Contact.find(); res.json(contacts); }); router.post('/', async (req, res) => { const newContact = new Contact(req.body); await newContact.save(); res.status(201).json(newContact); }); router.put('/:id', async (req, res) => { const updated = await Contact.findByIdAndUpdate(req.params.id, req.body, { new: true }); res.json(updated); }); router.delete('/:id', async (req, res) => { await Contact.findByIdAndDelete(req.params.id); res.status(204).send(); });

4. Frontend with React

Folder Structure:

bash
/crm-frontend /components /pages App.jsx main.jsx

Main App UI

jsx
import { useEffect, useState } from 'react'; import axios from 'axios'; function App() { const [contacts, setContacts] = useState([]); const [newContact, setNewContact] = useState({ name: '', email: '', phone: '', status: 'Lead' }); useEffect(() => { axios.get('http://localhost:5000/api/contacts') .then(res => setContacts(res.data)); }, []); const handleAdd = () => { axios.post('http://localhost:5000/api/contacts', newContact) .then(res => setContacts([...contacts, res.data])); }; return ( <div className="p-6"> <h1 className="text-2xl mb-4">Simple CRM</h1> <input className="border" placeholder="Name" onChange={e => setNewContact({ ...newContact, name: e.target.value })} /> <input className="border ml-2" placeholder="Email" onChange={e => setNewContact({ ...newContact, email: e.target.value })} /> <button onClick={handleAdd} className="bg-blue-500 text-white px-3 py-1 ml-2">Add</button> <ul className="mt-4"> {contacts.map(c => ( <li key={c._id} className="border-b py-2"> {c.name} - {c.status} </li> ))} </ul> </div> ); } export default App;

Optional Enhancements

  • Add filtering/search for contacts.

  • Use Redux or React Context for state management.

  • Add toast notifications.

  • Deploy backend to Render or Railway and frontend to Netlify or Vercel.

  • Add email reminders or follow-up tasks.

  • Create analytics dashboard for sales conversion.


Security & Production Tips

  • Use bcrypt for password hashing.

  • Store JWT secrets in environment variables.

  • Implement user-specific data isolation (only show contacts created by logged-in users).

  • Enable CORS properly and add rate limiting.


This CRM can be extended gradually as your business grows. It offers a great learning experience while fulfilling essential contact and sales tracking needs.

Share this Page your favorite way: Click any app below to share.

Enter your email below to join The Palos Publishing Company Email List

We respect your email privacy

Categories We Write About