Slack emoji bulk upload
Drop a folder of images, GIFs, or a zip. Every file comes out under 128 KB with a name Slack accepts, previewed at 22 px, in one zip. Then add them all to your workspace with an uploader we ran against a real one.
Add them all: the uploader
- Download the zip and unzip it.
- Open your workspace at app.slack.com in Chrome, Edge, or Firefox. The desktop app has no console.
- Open the console: Cmd+Option+J on a Mac, Ctrl+Shift+J on Windows. Chrome asks you to type
allow pastingthe first time. - Click Copy uploader, paste it, and press Enter.
- Drag the unzipped files onto the green panel. It adds about 20 a minute and lists each result.
Read the uploader before you paste it
// Slack emoji bulk upload from https://slack.green/en/tools/slack-emoji-bulk-upload
// Adds a panel to this Slack tab. Drop your emoji files on it. Each file name
// (without .png/.gif) becomes the emoji name. It calls only Slack's emoji.list
// and emoji.add, the same calls as the Add emoji dialog, as you.
(() => {
const GAP_MS = 3100
const teamId = (location.pathname.match(/\/client\/([TE][A-Z0-9]+)/) || [])[1]
const teams = JSON.parse(localStorage.getItem('localConfig_v2') || '{}').teams || {}
const team = teams[teamId] || Object.values(teams)[0]
if (!team || !team.token) return alert('Open your workspace at app.slack.com in this tab first.')
const wait = (ms) => new Promise((r) => setTimeout(r, ms))
async function api(method, fields) {
const body = new FormData()
body.append('token', team.token)
for (const [key, value] of Object.entries(fields)) body.append(key, value)
for (;;) {
const res = await fetch('/api/' + method, { method: 'POST', body, credentials: 'include' })
const retry = Number(res.headers.get('retry-after')) || 30
if (res.status === 429) { await wait(retry * 1000); continue }
const json = await res.json()
if (json.error === 'ratelimited') { await wait(retry * 1000); continue }
return json
}
}
const MEANING = {
error_name_taken: 'name already used',
error_name_taken_i18n: 'name is a built-in emoji',
error_bad_name_i18n: 'name has characters Slack refuses',
error_lower_case_names_only: 'name must be lowercase',
resized_but_still_too_large: 'file too big, even after Slack resized it',
no_permission: 'you are not allowed to add emoji here',
not_allowed: 'you are not allowed to add emoji here',
restricted_action: 'you are not allowed to add emoji here',
}
const panel = document.createElement('div')
panel.style.cssText = 'position:fixed;top:16px;right:16px;z-index:99999;width:340px;max-height:80vh;overflow:auto;background:#fff;color:#1d1c1d;border:2px dashed #22c55e;border-radius:10px;padding:14px;font:14px/1.4 sans-serif;box-shadow:0 8px 24px rgba(0,0,0,.25)'
const title = document.createElement('b')
title.textContent = 'Emoji upload to ' + (team.name || team.domain || 'this workspace')
const hint = document.createElement('p')
hint.textContent = 'Drop your emoji files here, or choose them.'
const input = document.createElement('input')
input.type = 'file'
input.multiple = true
input.accept = 'image/png,image/gif,image/jpeg,image/webp'
const close = document.createElement('button')
close.textContent = 'Close'
close.style.cssText = 'float:right'
close.onclick = () => panel.remove()
const log = document.createElement('ol')
log.style.cssText = 'padding-left:20px;margin:8px 0 0'
panel.append(close, title, hint, input, log)
document.body.append(panel)
const line = (text) => {
const li = document.createElement('li')
li.textContent = text
log.append(li)
return li
}
async function upload(files) {
const list = await api('emoji.list', {})
if (!list.ok) return line('Could not read the emoji list: ' + list.error)
const existing = new Set(Object.keys(list.emoji || {}))
const counts = { added: 0, skipped: 0, failed: 0 }
const images = [...files].filter((f) => /\.(png|gif|jpe?g|webp)$/i.test(f.name))
for (const [i, file] of images.entries()) {
const name = file.name.replace(/\.[a-z]+$/i, '')
hint.textContent = 'Uploading ' + (i + 1) + ' of ' + images.length + '...'
if (existing.has(name)) { line(':' + name + ': already in the workspace, skipped'); counts.skipped++; continue }
const row = line(':' + name + ': uploading')
const res = await api('emoji.add', { mode: 'data', name, image: file })
if (res.ok) { row.textContent = ':' + name + ': added'; existing.add(name); counts.added++ }
else { row.textContent = ':' + name + ': failed, ' + (MEANING[res.error] || res.error); counts.failed++ }
if (i < images.length - 1) await wait(GAP_MS)
}
hint.textContent = 'Done: ' + counts.added + ' added, ' + counts.skipped + ' skipped, ' + counts.failed + ' failed.'
}
input.onchange = () => upload(input.files)
panel.ondragover = (e) => e.preventDefault()
panel.ondrop = (e) => { e.preventDefault(); upload(e.dataTransfer.files) }
})()
Add them one at a time
- In Slack, open the emoji picker in the message box.
- Click Add Emoji, then Upload Image, and pick one file.
- Paste its name with the copy button on its card, and click Save.
Slack's dialog takes one file per upload, so this is the way on the desktop app or when you would rather not paste code.
How to bulk upload emoji to Slack
- Drop your images, GIFs, or a zip of them into the box above. Until you do, a sample pack shows what gets fixed.
- Check each card. GIFs are re-encoded under 128 KB at 128 px with at most 50 frames, and each name is cleaned up and moved off built-in names. Edit any name, or add a prefix for the whole pack.
- Look at the pack at 22 px on light and dark, then download the zip. Each file is named for the emoji it becomes, and emoji-names.txt lists the codes.
- Open your workspace at app.slack.com, paste the uploader into the browser console, and drag the unzipped files onto its panel. Or add them one at a time with Add Emoji in the emoji picker.
Frequently asked questions
Can you bulk upload emoji to Slack?
Not in Slack's own dialog. Add Emoji takes one file and one typed name per upload; we checked its file picker on 30 September 2026. Bulk tools all make the same emoji.add call the dialog makes, once per file. The uploader on this page does that from your Slack tab, skips names the workspace already has, and adds about 20 a minute, because Slack answered HTTP 429 with Retry-After 30 when we went faster. In our test it added 8 emoji from this page in 33 seconds.
Why does Slack say "This image is a bit too big, even after resizing"?
Slack shrinks every upload to 128x128 and then needs it under 128 KB. Stills always get there: a 3 MB, 1000x1000 PNG went in fine in our test. GIFs are the ones that fail. A 277 KB GIF of 12 noisy frames and a 1.8 MB 256 px GIF both got this error. This page re-encodes each GIF with fewer colors, and fewer frames if needed, until it is under 128 KB, so Slack accepts it.
What names can a Slack emoji have?
Lowercase letters, numbers, hyphens, and underscores. Letters from other scripts work too; :おつかれ: and :한글: were accepted and shown in messages. Capital letters, spaces, dots, and most punctuation are refused. Built-in names such as :thumbsup: are taken, and names longer than 100 characters are cut to 100 without a warning. The builder applies all of these rules and gives a taken name the next free -2, -3 suffix.
Is the uploader safe to paste into the console?
Read it first; the full script is on this page. It uses the session of the Slack tab you paste it into, calls only that tab's own /api/emoji.list and /api/emoji.add, and sends nothing to slack.green. It can do only what you can do in the Add emoji dialog, so if your admin limits who adds emoji, it fails the same way. Chrome makes you type allow pasting before the first paste because pasted code runs as you, which is why you should only paste code you can read.
Related reading
Slack Green does one thing: it keeps your Slack presence dot green while you are actually working, so a quiet keyboard does not mark you away. Try it free.