Vue.js Integration Guide
Install IssueCapture in Vue.js
Add bug reporting to a Vue 3 app. Works with Vite, Composition API, Vue Router, and Pinia.
Quick Summary
Time: ~10 minutes (incl. Jira OAuth)
Prerequisites: Vue 3+, Vite, Jira Cloud account
Method: Script tag or main.ts inject
Works with: Vue 3, Vite, Vue Router, Pinia
Prerequisites
- Vue 3 with Composition API
- Vite (recommended) or another ESM-compatible bundler
- Jira Cloud account (Software or JSM)
- IssueCapture account (free — includes 10 issues/month)
Step-by-Step Installation
Follow these steps to get up and running in minutes.
1
Get Your API Key
Sign up for IssueCapture and grab your widget API key
- Go to issuecapture.com/signup and create a free account
- Connect your Jira instance via OAuth (about 30 seconds)
- Open the Widgets page and create a new widget
- Click the "Keys" tab on your widget and copy the API key (starts with "ic_")
2
Add the API key to .env
Vite reads VITE_-prefixed vars via import.meta.env
- Vite only exposes variables that start with VITE_ to the browser bundle
- Access at runtime via `import.meta.env.VITE_ISSUECAPTURE_API_KEY`
- Add .env to .gitignore — don't commit the key
- Restart `vite` after adding env vars
# .env (Vite — recommended for Vue 3)
VITE_ISSUECAPTURE_API_KEY=ic_your_api_key_here3
Install via index.html (simplest)
Hardcode the script tag — fine if you can paste the key directly
- The widget is an ESM module — type="module" is required
- Vite does NOT replace tokens in index.html, so paste the key directly here
- For a runtime key from .env (recommended for repo-friendly setups), use Step 4 instead
- Place the snippet at the end of <body> so it never blocks first paint
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Your Vue App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<!-- IssueCapture Widget (ESM module) -->
<script type="module">
import IssueCapture from 'https://issuecapture.com/widget.js';
IssueCapture.init({ apiKey: 'ic_your_api_key_here' });
</script>
</body>
</html>4
Or load from main.ts so .env supplies the key
Inject the script after the Vue app mounts — keeps the key out of HTML
- `import.meta.env.VITE_ISSUECAPTURE_API_KEY` is replaced at build time with the value from .env
- Adding a `<script type="module">` programmatically with inline textContent DOES execute when appended — this is the supported pattern
- Putting this in main.ts keeps the widget at app level (no component teardown to worry about)
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
// IssueCapture — runs once on app boot
const apiKey = import.meta.env.VITE_ISSUECAPTURE_API_KEY
if (apiKey) {
const s = document.createElement('script')
s.type = 'module'
// Inline ESM — runs on append and gets the runtime value of apiKey
s.textContent = `
import IssueCapture from 'https://issuecapture.com/widget.js';
IssueCapture.init({ apiKey: '${apiKey}' });
`
document.body.appendChild(s)
}5
Trigger the widget from your own button
Skip the floating button and open the modal from a Vue component
- Optional-chain `window.IssueCapture?.open()` so it never throws if the script hasn't loaded yet
- You can use this alongside the floating button, or set `trigger: '#your-id'` on init to disable the floating one
- aria-label keeps the button accessible even if you go icon-only
<!-- src/components/BugReportButton.vue -->
<script setup lang="ts">
declare global {
interface Window {
IssueCapture?: {
open: () => void
close: () => void
updateConfig: (cfg: Record<string, unknown>) => void
}
}
}
const open = () => window.IssueCapture?.open()
</script>
<template>
<button
@click="open"
class="bug-report-btn"
aria-label="Report an issue"
>
Report an issue
</button>
</template>6
Pre-fill the logged-in user (optional)
When the user object changes, push their name and email through
- Use updateConfig for runtime changes — init should only fire once per page
- `immediate: true` runs the watcher once on setup so currently-logged-in users get pre-filled immediately
- Pre-fills are visible to the user before they submit — they can still edit them
<!-- src/App.vue -->
<script setup lang="ts">
import { watch } from 'vue'
import { useAuth } from './composables/useAuth' // your own auth composable
const { user } = useAuth()
// Whenever user changes, tell the widget who's reporting.
// updateConfig is cheap — call it freely. Do NOT re-call init().
watch(user, (next) => {
if (!next) return
window.IssueCapture?.updateConfig({
user: { name: next.name, email: next.email },
})
}, { immediate: true })
</script>
<template>
<div id="app"><!-- your app --></div>
</template>7
Verify it works
Quick sanity check end-to-end
- Start the dev server: `npm run dev` or `pnpm dev`
- Open your app — you should see the floating Report Issue button
- Open DevTools console and type `IssueCapture` — you should see the widget object
- Click the button, submit a test issue, and confirm it lands in Jira
Troubleshooting
Common integration issues and how to solve them.
TypeScript: Property 'IssueCapture' does not exist on type 'Window'
Add a global declaration to your project (src/types/issuecapture.d.ts)
- Create `src/types/issuecapture.d.ts` declaring `IssueCapture` on the global Window interface
- Make sure that path is covered by `tsconfig.json` `include`
- See the IssueCaptureAPI surface in the docs for the full method list
Environment variables aren't reaching the browser
- Vite only exposes variables prefixed with `VITE_`
- Read them via `import.meta.env.VITE_YOUR_VAR` — `process.env` is unavailable in browser code
- Restart `vite` after editing .env
- Ensure `.env` lives next to `vite.config.ts` in the project root
Widget loads twice when navigating with Vue Router
- Initialise the widget once in main.ts (not in a route-level component)
- If you must run it from a component, guard with `if (window.IssueCapture) return` before injecting
- For per-route tweaks, use `IssueCapture.updateConfig()` — never re-call `init()`
"Domain not allowed" error (often shows up as CORS)
- In the IssueCapture dashboard, open your widget and click the Domains tab
- Add `localhost:5173` (Vite default) for local dev
- Add your production domain — both www and non-www if you serve both
- Subdomains need to be listed individually
Ready to get started?
Free plan includes 10 issues/month. No card needed — connect Jira and you're done.