Examples
Tailwind CSS Integration
import {
FeedbackProvider,
useFeedback,
} from '@pullreque.st/button/react/headless'
import { useState } from 'react'
function FeedbackModal() {
const { submit, isSubmitting, error } = useFeedback()
const [open, setOpen] = useState(false)
const [message, setMessage] = useState('')
return (
<>
<button
onClick={() => setOpen(true)}
className="fixed bottom-4 right-4 bg-gradient-to-r from-purple-500 to-pink-500 text-white px-6 py-3 rounded-full shadow-lg hover:shadow-xl transition-all"
>
💬 Feedback
</button>
{open && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white rounded-2xl p-6 w-full max-w-md">
<h2 className="text-2xl font-bold mb-4">
Send Feedback
</h2>
<form
onSubmit={async e => {
e.preventDefault()
await submit({ message })
setOpen(false)
setMessage('')
}}
>
<textarea
value={message}
onChange={e => setMessage(e.target.value)}
placeholder="Tell us what you think..."
className="w-full border rounded-lg p-4 mb-4 min-h-32"
/>
{error && (
<p className="text-red-500 text-sm mb-4">
{error.message}
</p>
)}
<div className="flex gap-2">
<button
type="button"
onClick={() => setOpen(false)}
className="flex-1 px-4 py-2 border rounded-lg"
>
Cancel
</button>
<button
type="submit"
disabled={isSubmitting}
className="flex-1 px-4 py-2 bg-purple-500 text-white rounded-lg"
>
{isSubmitting ? 'Sending...' : 'Submit'}
</button>
</div>
</form>
</div>
</div>
)}
</>
)
}With Custom Metadata
Track additional context with feedback:
const { submit } = useFeedback()
await submit({
message: 'Feature request',
meta: {
page: window.location.pathname,
userAgent: navigator.userAgent,
viewport: `${window.innerWidth}x${window.innerHeight}`,
timestamp: new Date().toISOString(),
version: '1.2.3',
},
})Error Handling
const { submit, error, reset } = useFeedback({
onError: error => {
// Log to error tracking service
console.error('Feedback submission failed:', error)
// Show toast notification
toast.error('Failed to submit feedback. Please try again.')
},
onSuccess: response => {
toast.success('Thank you for your feedback!')
},
})
// Clear error manually
useEffect(() => {
if (error) {
const timer = setTimeout(() => reset(), 5000)
return () => clearTimeout(timer)
}
}, [error, reset])VitePress Integration
Add to
.vitepress/config.ts:import { defineConfig } from 'vitepress'
export default defineConfig({
head: [
['script', { src: 'https://pullreque.st/cdn/button.js' }],
[
'script',
{},
`PullrequeStButton.init({
projectKey: 'prj_pk_YOUR_KEY',
position: 'bottom-right'
})`,
],
],
})Next.js App Router
// app/layout.tsx
import { FeedbackProvider } from '@pullreque.st/button/react/headless'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<FeedbackProvider projectKey="prj_pk_YOUR_KEY">
{children}
</FeedbackProvider>
</body>
</html>
)
}
// app/components/feedback-button.tsx
;('use client')
import { useFeedback } from '@pullreque.st/button/react/headless'
import { useState } from 'react'
export function FeedbackButton() {
const { submit, isSubmitting } = useFeedback()
const [message, setMessage] = useState('')
return (
<form
onSubmit={async e => {
e.preventDefault()
await submit({ message })
setMessage('')
}}
>
<textarea
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send'}
</button>
</form>
)
}Vanilla JavaScript
<!DOCTYPE html>
<html>
<head>
<title>Feedback Example</title>
</head>
<body>
<form id="feedback-form">
<textarea id="message" placeholder="Your feedback..."></textarea>
<button type="submit">Send</button>
</form>
<script src="https://pullreque.st/cdn/button.js"></script>
<script>
const client = PullrequeStButton.createFeedbackClient({
projectKey: 'prj_pk_YOUR_KEY',
})
document
.getElementById('feedback-form')
.addEventListener('submit', async e => {
e.preventDefault()
const message = document.getElementById('message').value
try {
await client.submit({ message })
alert('Thank you!')
} catch (error) {
alert('Error: ' + error.message)
}
})
</script>
</body>
</html>Need Help?
- Documentation: https://pullreque.st/documentation
- Email: cody@isolated.tech