Pullreque.st

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?