Pullreque.st

Headless React

Best for: React applications that want complete UI control

Pros

  • ✅ Full control over UI/UX
  • ✅ Works with Tailwind, ShadCN, CSS Modules
  • ✅ Type-safe with TypeScript
  • ✅ Smaller bundle size (no UI code)

Installation

import { FeedbackProvider, useFeedback } from '@pullreque.st/button/react/headless'

Basic Usage

// 1. Wrap your app with FeedbackProvider
<FeedbackProvider projectKey="prj_pk_YOUR_KEY">
  <App />
</FeedbackProvider>

// 2. Use the useFeedback hook in any component
function MyComponent() {
  const { submit, isSubmitting, error, data, reset } = useFeedback({
    onSuccess: (response) => {
      console.log('Feedback submitted!', response)
    },
    onError: (error) => {
      console.error('Submission failed:', error)
    },
    autoCaptureUrl: true  // Auto-capture page URL (default: true)
  })

  // Your custom UI implementation
}

Example: 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>
      )}
    </>
  )
}

Next Steps