Stop emailing yourself code snippets or using sketchy online clipboards to move text from your phone to your PC.
I built a lightweight, local-only clipboard-sharing webapp in Node.js.
✅ No logins
✅ No cloud tracking
✅ Bulletproof mobile sync (no cache bugs)
Here is the full code to build it in 60 seconds 🧵👇
***
## Install Instructions
🛠️ **SETUP**
It takes 60 seconds. You just need Node.js installed.
```bash
mkdir clip-share && cd clip-share
npm init -y
npm install express
mkdir public
```
Create two files: `server.js` and `public/index.html` using the code in the next tweets.
Then just run:
`node server.js`
***
## The Backend
⚙️ **THE SERVER (server.js)**
A dead-simple Express API that holds your text in memory.
```javascript
const express = require('express');
const app = express();
let snippet = '';
app.use(express.json());
app.use(express.static('public'));
app.get('/api/snippet', (req, res) => res.json({ text: snippet }));
app.post('/api/snippet', (req, res) => {
snippet = req.body.text || '';
res.json({ success: true });
});
app.listen(3000, '0.0.0.0', () => console.log('Listening on :3000'));
```
***
The Frontend
📱 **THE FRONTEND (public/index.html)**
A robust UI. It uses URL cache-busting to defeat aggressive mobile caching, detects typing (so polling doesn't overwrite your work), and forces a sync when you switch back to the tab.
```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clipboard Share</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: sans-serif; padding: 20px; background:
#f5f5f5; }
.container { max-width: 800px; margin: 0 auto; }
h1 { margin-bottom: 20px; color: #333; }
textarea {
width: 100%; height: 300px; padding: 15px;
font-family: monospace; font-size: 16px;
border: 2px solid
#ddd; border-radius: 8px;
resize: vertical; background: white;
}
textarea:focus { outline: none; border-color:
#4CAF50; }
.btn-group { display: flex; gap: 10px; margin-top: 15px; flex-wrap: wrap; }
button {
flex: 1; min-width: 100px; padding: 15px; font-size: 16px;
border: none; border-radius: 6px; cursor: pointer; font-weight: 600; color: white;
}
.btn-save { background:
#4CAF50; }
.btn-copy { background:
#2196F3; }
.btn-clear { background:
#e53935; }
.status { margin-top: 15px; text-align: center; color: #666; font-size: 14px; height: 20px; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<h1>📋 Clipboard Share</h1>
<textarea id="t" placeholder="Paste or type text..."></textarea>
<div class="btn-group">
<button class="btn-save" onclick="save()">Save & Share</button>
<button class="btn-copy" onclick="copy()">Copy</button>
<button class="btn-clear" onclick="clearT()">Clear</button>
</div>
<div id="status" class="status"></div>
</div>
<script>
const t = document.getElementById('t');
const statusEl = document.getElementById('status');
let isTyping = false;
let typingTimer;
t.addEventListener('input', () => {
isTyping = true;
clearTimeout(typingTimer);
typingTimer = setTimeout(() => { isTyping = false; }, 2000);
});
function showStatus(msg) {
statusEl.textContent = msg;
setTimeout(() => { if(statusEl.textContent === msg) statusEl.textContent = ''; }, 2000);
}
async function load() {
if (isTyping || document.activeElement === t) return;
try {
const res = await fetch('/api/snippet?v='
Date.now(), { cache: 'no-store' });
const d = await res.json();
if (d.text !== t.value) t.value = d.text;
} catch (e) {
showStatus('Connection lost...');
}
}
async function save() {
try {
await fetch('/api/snippet', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: t.value })
});
showStatus('Saved!');
} catch (e) {
showStatus('Save failed!');
}
}
function copy() {
if (!t.value) return;
// 1. Try modern API (works on computer/localhost)
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(t.value)
.then(() => showStatus('Copied!'))
.catch(() => fallbackCopy());
} else {
// 2. Fallback for mobile HTTP
fallbackCopy();
}
}
function fallbackCopy() {
const temp = document.createElement('textarea');
temp.value = t.value;
temp.style.position = 'fixed';
temp.style.opacity = '0';
document.body.appendChild(temp);
temp.select();
let success = false;
try {
success = document.execCommand('copy');
} catch (e) {}
document.body.removeChild(temp);
if (success) {
showStatus('Copied!');
} else {
// 3. Last resort: highlight text for manual long-press
t.focus();
t.select();
showStatus('Text selected - long press to copy');
}
}
async function clearT() {
t.value = '';
await save();
}
document.addEventListener('visibilitychange', () => {
if (!document.hidden) load();
});
t.addEventListener('blur', () => load());
load();
setInterval(load, 3000);
</script>
</body>
</html>
```
***
## How to Use It
🚀 **HOW TO USE**
1. Run `node server.js` on your PC.
2. Open `http://localhost:3000` on your PC browser.
3. Find your PC's local IP (`ipconfig` on Windows, `ip addr` or `hostname -I` on Linux/Mac).
4. Open `http://YOUR_IP:3000` on your phone.
Paste on your phone, hit Save, and grab it on your PC instantly.
Bookmark this for your next late-night coding session! 🔖