NPM Package Guide
Integrate Classy Comments into your JavaScript/TypeScript application
Installation
npm install @classy-comments/coreDon't use npm?
If you don't use build tools, check out our Direct Install (CDN) docs for a simple copy/paste solution that works on any website.
Requirements
- Node.js 16+ (or modern browser)
- Framework: React, Vue, Angular, Svelte, or vanilla JS
- TypeScript 4.5+ (optional, but recommended)
Quick Start
import { ClassyComments } from '@classy-comments/core'
import '@classy-comments/core/styles.css'
const classy = new ClassyComments({
apiBaseUrl: 'https://api.classycomments.com',
apiKey: 'cc_live_sk_your_api_key',
// REQUIRED: Specify which form and field to attach to
formSelector: '#comment-form', // CSS selector for the form
fieldSelector: '#comment-text' // CSS selector for the textarea
})
await classy.init()Settings come from your dashboard
When using AI mode, refinement settings (formality, tone, profanity handling, etc.) are fetched from your dashboard configuration. You don't pass them here—they're managed centrally.
Specifying Which Field to Enhance
This is the most important configuration! You must tell Classy Comments which form and field to attach to.
Method 1: CSS Selectors (Recommended)
const classy = new ClassyComments({
apiKey: 'your_api_key',
formSelector: '#comment-form', // Find the form by ID
fieldSelector: '#comment-text' // Find the textarea by ID
})Method 2: Field Name
If your field has a name attribute:
const classy = new ClassyComments({
apiKey: 'your_api_key',
formSelector: '.comment-form',
fieldSelector: 'comment' // Will find [name="comment"]
})Examples by Framework
React
import { useEffect } from 'react'
import { ClassyComments } from '@classy-comments/core'
import '@classy-comments/core/styles.css'
function CommentForm() {
useEffect(() => {
const classy = new ClassyComments({
apiBaseUrl: 'https://api.classycomments.com',
apiKey: process.env.REACT_APP_CLASSY_API_KEY,
formSelector: '#my-comment-form',
fieldSelector: '#comment-textarea'
})
classy.init()
}, [])
return (
<form id="my-comment-form">
<textarea
id="comment-textarea"
name="comment"
rows={5}
/>
<button type="submit">Post Comment</button>
</form>
)
}Vue
<template>
<form id="comment-form">
<textarea id="comment-field" v-model="comment" />
<button type="submit">Submit</button>
</form>
</template>
<script>
import { ClassyComments } from '@classy-comments/core'
import '@classy-comments/core/styles.css'
export default {
mounted() {
const classy = new ClassyComments({
apiKey: process.env.VUE_APP_CLASSY_API_KEY,
formSelector: '#comment-form',
fieldSelector: '#comment-field'
})
classy.init()
}
}
</script>Vanilla JavaScript
<form id="contact-form">
<textarea id="message-field" name="message"></textarea>
<button type="submit">Send</button>
</form>
<script type="module">
import { ClassyComments } from '@classy-comments/core'
import '@classy-comments/core/styles.css'
const classy = new ClassyComments({
apiKey: 'cc_live_sk_your_key',
formSelector: '#contact-form',
fieldSelector: '#message-field'
})
await classy.init()
</script>Configuration Options
Constructor Options
| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Yes | Your API key from the dashboard |
| formSelector | string | Yes | CSS selector for the form |
| fieldSelector | string | Yes | CSS selector or name of the textarea |
| apiBaseUrl | string | No | Default: https://api.classycomments.com |
| onApprove | function | No | Callback when comment is approved |
| validateBeforeIntercept | boolean | No | Check HTML5 validation before showing modal (default: true) |
| onApprove | function | No | Callback when comment is approved |
| onCancel | function | No | Callback when user cancels |
Form Validation Control
By default, Classy Comments respects HTML5 form validation. If a form has validation errors (required fields, invalid email, etc.), the browser will show validation messages BEFORE the modal appears.
validateBeforeIntercept: true (default)
HTML5 validation runs first. If the form is invalid, browser shows validation errors and the modal won't appear. This is the recommended approach for most use cases.
validateBeforeIntercept: false
Modal appears immediately without checking HTML5 validation first. Use this if you want users to refine their comment before validating other fields.
const classy = new ClassyComments({
apiKey: 'your_key',
formSelector: '#contact-form',
fieldSelector: '#message',
validateBeforeIntercept: false // Skip HTML5 validation
})Manual Mode (SPAs)
Recommended for Vue, React, Angular, and other SPAs where you want full control over when refinement happens.
When to use Manual Mode:
- Vue.js, React, Angular, or other SPAs
- You prevent form submission with
.preventorpreventDefault() - Custom validation needs to run before refinement
- You want to decide when refinement happens
Basic Usage
const classy = new ClassyComments({
apiKey: 'your_api_key'
// No formSelector or fieldSelector = Manual Mode
})
await classy.init()
// In your submit handler
await classy.processCommentManual(commentText, {
onApprove: (refinedText) => {
// User approved - submit with refined text
submitToBackend(refinedText)
},
onCancel: () => {
// User cancelled - do nothing
}
})Vue 3 Example
<template>
<form @submit.prevent="handleSubmit">
<input v-model="authorName" required placeholder="Your name">
<textarea v-model="comment" required></textarea>
<button type="submit" :disabled="submitting">Submit</button>
</form>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ClassyComments } from '@classy-comments/core'
import '@classy-comments/core/styles.css'
const authorName = ref('')
const comment = ref('')
const submitting = ref(false)
const classyReady = ref(false)
const classyInstance = ref(null)
onMounted(async () => {
classyInstance.value = new ClassyComments({
apiKey: 'your-api-key'
})
await classyInstance.value.init()
classyReady.value = true
})
const handleSubmit = async () => {
if (!comment.value.trim() || submitting.value) return
submitting.value = true
if (classyReady.value && classyInstance.value) {
try {
await classyInstance.value.processCommentManual(comment.value, {
onApprove: async (refinedText) => {
comment.value = refinedText
classyReady.value = false // Prevent re-processing
await submitComment(refinedText)
classyReady.value = true
},
onCancel: () => {
submitting.value = false
}
})
return
} catch (error) {
console.error('Refinement error:', error)
}
}
await submitComment(comment.value)
}
const submitComment = async (text) => {
try {
await fetch('/api/comments', {
method: 'POST',
body: JSON.stringify({ author: authorName.value, comment: text })
})
comment.value = ''
authorName.value = ''
} finally {
submitting.value = false
}
}
</script>React Example
import { useState, useEffect, useRef } from 'react'
import { ClassyComments } from '@classy-comments/core'
import '@classy-comments/core/styles.css'
function CommentForm() {
const [author, setAuthor] = useState('')
const [comment, setComment] = useState('')
const [submitting, setSubmitting] = useState(false)
const classyRef = useRef(null)
const readyRef = useRef(false)
useEffect(() => {
const init = async () => {
classyRef.current = new ClassyComments({
apiKey: 'your-api-key'
})
await classyRef.current.init()
readyRef.current = true
}
init()
}, [])
const submitComment = async (text) => {
try {
await fetch('/api/comments', {
method: 'POST',
body: JSON.stringify({ author, comment: text })
})
setAuthor('')
setComment('')
} finally {
setSubmitting(false)
}
}
const handleSubmit = async (e) => {
e.preventDefault()
if (!comment.trim() || submitting) return
setSubmitting(true)
if (readyRef.current && classyRef.current) {
try {
await classyRef.current.processCommentManual(comment, {
onApprove: async (refined) => {
setComment(refined)
readyRef.current = false
await submitComment(refined)
readyRef.current = true
},
onCancel: () => setSubmitting(false)
})
return
} catch (error) {
console.error(error)
}
}
await submitComment(comment)
}
return (
<form onSubmit={handleSubmit}>
<input value={author} onChange={e => setAuthor(e.target.value)} />
<textarea value={comment} onChange={e => setComment(e.target.value)} />
<button disabled={submitting}>Submit</button>
</form>
)
}Important Notes
Prevent Re-processing
In onApprove, temporarily disable Classy Comments to prevent the refined text from being processed again:
onApprove: async (refined) => {
classyReady = false // Disable
await submitComment(refined)
classyReady = true // Re-enable
}Always Provide Fallback
If refinement fails, still allow comment submission:
if (classyReady) {
try {
await classy.processCommentManual(text, callbacks)
return // Exit if successful
} catch (error) {
console.error(error)
// Fall through to normal submission
}
}
// Fallback: submit without refinement
await submitComment(text)Use Cases for Manual Mode
- Vue, React, Angular SPAs with controlled components
- Custom validation that needs to run before refinement
- Chat applications and messaging interfaces
- Rich text editors (CKEditor, TinyMCE, Quill)
- Multi-step forms where refinement happens mid-flow
- Mobile apps (React Native, Ionic, Capacitor)
Where Do Refinement Settings Come From?
Dashboard Configuration (Recommended)
Configure formality, tone, profanity handling, and all other settings in your dashboard. The npm package automatically fetches these settings when you call init().
Benefits of dashboard configuration:
- Change settings without redeploying your app
- Same settings across all your tools (WordPress, extension, npm)
- Test different configs easily
- A/B test refinement strategies
TypeScript Support
Full TypeScript support with type definitions included:
import { ClassyComments, ClassyCommentsOptions } from '@classy-comments/core'
const options: ClassyCommentsOptions = {
apiKey: 'cc_live_sk_your_key',
apiBaseUrl: 'https://api.classycomments.com',
formSelector: '#comment-form',
fieldSelector: '#comment-field',
onApprove: (approvedText: string) => {
console.log(approvedText)
}
}
const classy = new ClassyComments(options)
await classy.init()Customizing Styles
The modal uses CSS variables that you can override:
:root {
--classy-primary-color: #9333ea; /* Purple */
--classy-accent-color: #f59e0b; /* Gold */
--classy-gold-light: #fbbf24;
--classy-purple-dark: #7c3aed;
/* ... and more */
}
/* Override modal background */
.classy-comments-overlay {
backdrop-filter: blur(12px) !important;
}
/* Customize buttons */
.classy-btn-primary {
background: your-gradient !important;
}Troubleshooting
Modal not appearing
- Verify
formSelectorandfieldSelectorare correct - Check browser console for errors
- Ensure CSS is imported:
import '@classy-comments/core/styles.css' - Verify
init()completed successfully
Can't find the field
- Try using
document.querySelector('your-selector')in console to test - Make sure the element exists in the DOM when
init()is called - For SPAs, wait for component mount before calling
init()
API errors
- Check API key is correct
- Verify you haven't exceeded quota
- Check Network tab for API response details
Complete Example
Check the examples in the npm package documentation and our demo site for working implementations.
Need Help?
For npm package support: