React Integration
Add moderation-aware components to your React app.
Installation
bash
npm install @nextauralabs/vettly-react @nextauralabs/vettly-sdkQuick Start
ModeratedTextarea
A textarea that checks content in real-time as users type.
tsx
import { ModeratedTextarea } from '@nextauralabs/vettly-react'
import '@nextauralabs/vettly-react/styles.css'
function CommentForm() {
const [value, setValue] = useState('')
const [isBlocked, setIsBlocked] = useState(false)
return (
<form>
<ModeratedTextarea
apiKey={process.env.NEXT_PUBLIC_VETTLY_API_KEY}
value={value}
onChange={setValue}
onModerationResult={(result) => {
setIsBlocked(result.action === 'block')
}}
placeholder="Write your comment..."
debounceMs={500}
/>
<button type="submit" disabled={isBlocked}>
Submit
</button>
</form>
)
}ModeratedImageUpload
Moderate images before upload.
tsx
import { ModeratedImageUpload } from '@nextauralabs/vettly-react'
import '@nextauralabs/vettly-react/styles.css'
function AvatarUpload() {
return (
<ModeratedImageUpload
apiKey={process.env.NEXT_PUBLIC_VETTLY_API_KEY}
onUpload={(file, result) => {
if (result.action === 'allow') {
// Upload to your storage
uploadToS3(file)
}
}}
onBlock={(result) => {
alert('Image contains inappropriate content')
}}
maxSizeMB={5}
accept="image/*"
/>
)
}ModeratedVideoUpload
Moderate video content.
tsx
import { ModeratedVideoUpload } from '@nextauralabs/vettly-react'
function VideoUpload() {
return (
<ModeratedVideoUpload
apiKey={process.env.NEXT_PUBLIC_VETTLY_API_KEY}
onUpload={(file, result) => {
console.log('Video moderation:', result)
}}
onBlock={(result) => {
console.log('Video blocked:', result.categories)
}}
maxSizeMB={100}
/>
)
}useModeration Hook
For custom implementations:
tsx
import { useModeration } from '@nextauralabs/vettly-react'
function CustomInput() {
const [text, setText] = useState('')
const { check, result, isLoading, error } = useModeration({
apiKey: process.env.NEXT_PUBLIC_VETTLY_API_KEY,
debounceMs: 300
})
const handleChange = (e) => {
const value = e.target.value
setText(value)
check(value, 'text')
}
return (
<div>
<input
value={text}
onChange={handleChange}
className={result?.action === 'block' ? 'border-red-500' : ''}
/>
{isLoading && <span>Checking...</span>}
{result?.action === 'block' && (
<span className="text-red-500">
Content not allowed
</span>
)}
</div>
)
}ModerationProvider
Share a client across components:
tsx
import { ModerationProvider } from '@nextauralabs/vettly-react'
function App() {
return (
<ModerationProvider apiKey={process.env.NEXT_PUBLIC_VETTLY_API_KEY}>
<CommentForm />
<ProfileEditor />
</ModerationProvider>
)
}
// Child components use the shared client
function CommentForm() {
const { check } = useModeration() // Uses provider's client
// ...
}Custom Styling
Override default styles:
css
/* Your CSS */
.moderated-textarea {
border: 2px solid #e5e7eb;
border-radius: 8px;
padding: 12px;
}
.moderated-textarea.blocked {
border-color: #ef4444;
background-color: #fef2f2;
}
.moderation-feedback {
font-size: 14px;
margin-top: 4px;
}
.feedback-block {
color: #dc2626;
}
.feedback-safe {
color: #16a34a;
}Or use Tailwind:
tsx
<ModeratedTextarea
className="w-full p-3 border-2 rounded-lg focus:ring-2"
feedbackClassName="text-sm mt-1"
// ...
/>Server-Side Validation
Always validate on the server too:
tsx
// Client component
function CommentForm() {
const handleSubmit = async (e) => {
e.preventDefault()
// Client already checked, but server validates again
const response = await fetch('/api/comments', {
method: 'POST',
body: JSON.stringify({ content: value })
})
if (response.status === 403) {
// Server blocked it
setError('Content not allowed')
}
}
return (
<form onSubmit={handleSubmit}>
<ModeratedTextarea
apiKey={process.env.NEXT_PUBLIC_VETTLY_API_KEY}
value={value}
onChange={setValue}
/>
<button type="submit">Submit</button>
</form>
)
}ts
// API route (server)
export async function POST(request: Request) {
const { content } = await request.json()
const result = await vettly.check({ content, contentType: 'text' })
if (result.action === 'block') {
return Response.json({ error: 'Blocked' }, { status: 403 })
}
// Save to database
}Props Reference
ModeratedTextarea
| Prop | Type | Description |
|---|---|---|
apiKey | string | Your Vettly API key |
value | string | Controlled value |
onChange | (value: string) => void | Change handler |
onModerationResult | (result: ModerationResult) => void | Callback with result |
debounceMs | number | Debounce delay (default: 500) |
policyId | string | Custom policy to use |
showFeedback | boolean | Show status indicator |
placeholder | string | Placeholder text |
disabled | boolean | Disable input |
ModeratedImageUpload
| Prop | Type | Description |
|---|---|---|
apiKey | string | Your Vettly API key |
onUpload | (file: File, result: ModerationResult) => void | Success callback |
onBlock | (result: ModerationResult) => void | Block callback |
onError | (error: Error) => void | Error callback |
maxSizeMB | number | Max file size |
accept | string | Accepted file types |
policyId | string | Custom policy to use |
Next Steps
- Next.js Integration - Full Next.js setup
- Custom Policies - Define moderation rules
- Styling Guide - Customize appearance