Examples
Realistic end-to-end workflows for the CLI.
Bootstrapping a new project
You want a working email editor in your app, with a starter design and the project pre-connected to your Unlayer account.
# 1. Sign in
unlayer login
# 2. Scaffold (Next.js + product starter)
unlayer init my-emails -f nextjs -t product
# 3. Run it
cd my-emails
npm run dev
# → http://localhost:3000/editor
The scaffold writes unlayer.config.json with your projectId, so any future CLI command run from inside the project automatically scopes to it:
unlayer template list # lists templates in *this* project
unlayer pull # pulls them into ./output (the outputDir written by `unlayer init`)
Git-based template workflow
You want templates checked into your repo so design changes are visible in PRs.
One-time setup
cd your-app
unlayer login
unlayer project use 17 # pick the project
unlayer pull --design-only # writes designs into ./templates
git add templates
git commit -m "Initial template snapshot"
Daily flow
# Designer makes changes in the visual editor → save in Unlayer
unlayer pull --design-only
git diff # → see exactly what changed
git commit -m "Sync templates"
CI gate (catch drift)
Add to your CI:
unlayer pull --design-only --output-dir ./templates.remote
if ! diff -r ./templates ./templates.remote >/dev/null; then
echo "::error::Templates in repo are out of sync with Unlayer"
exit 1
fi
Or use unlayer diff per file. The JSON result has a totalChanges field; zero means no drift:
for f in templates/*.json; do
if ! unlayer diff "$f" --json | jq -e '.totalChanges == 0' >/dev/null; then
echo "Drifted: $f"
exit 1
fi
done
CI/CD (token-based)
Most CI workflows skip unlayer login entirely and pass the token via env var instead:
# .github/workflows/sync-templates.yml
name: Sync Unlayer Templates
on:
schedule:
- cron: '0 6 * * *' # daily 06:00 UTC
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 24 }
- run: npm install -g @unlayer/cli
- run: unlayer pull --design-only --json
env:
UNLAYER_TOKEN: ${{ secrets.UNLAYER_PAT }}
UNLAYER_PROJECT_ID: '17'
- uses: peter-evans/create-pull-request@v6
with:
title: 'chore: sync Unlayer templates'
commit-message: 'chore: sync Unlayer templates'
branch: unlayer-template-sync
The CLI auto-enables --non-interactive and --quiet when it sees the CI env var. Errors go to stderr; --json keeps stdout machine-parseable.
Pointing at a self-hosted deployment
Your team runs a self-hosted Unlayer deployment alongside the default api.unlayer.com. Save the alternate URLs as a named profile and switch with one command.
Define the profile
unlayer env add self-hosted \
--api-url https://unlayer-api.your-domain.com \
--console-url https://unlayer-console.your-domain.com \
--accounts-url https://unlayer-accounts.your-domain.com
Switch between deployments
unlayer env use self-hosted
unlayer template list # → reads from your self-hosted instance
unlayer pull --design-only
unlayer env use --none # back to the default URLs
unlayer template list # → reads from api.unlayer.com
Share profiles via git
Drop a unlayer.config.json at the repo root so the whole team can unlayer env use self-hosted without configuring URLs locally:
{
"projectId": 17,
"environments": {
"self-hosted": {
"apiUrl": "https://unlayer-api.your-domain.com",
"consoleUrl": "https://unlayer-console.your-domain.com",
"accountsUrl": "https://unlayer-accounts.your-domain.com"
}
}
}
Inspect a single template programmatically
TEMPLATE_ID=42
# Pull just the design JSON to stdout
unlayer template get "$TEMPLATE_ID" --design-only -o -
# Save it
unlayer template get "$TEMPLATE_ID" --design-only -o ./welcome.json
# Count rows in the design
unlayer template get "$TEMPLATE_ID" --design-only -o - \
| jq '.body.rows | length'
Troubleshooting in one command
When something is off, run status first:
unlayer status
It prints authentication state, API reachability and latency, the active workspace/project IDs, and your config file path. It doesn't require auth, so it works even when your token has expired. Exits with code 1 when something is wrong, otherwise 0.
If Authenticated: No, run unlayer login again.
See Also
- Authentication — token setup
- Configuration — env profiles, project config
- Errors — exit codes and what to do when commands fail