NPM Package Guide

Integrate Classy Comments into your JavaScript/TypeScript application

Installation

npm install @classy-comments/core

Don'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

OptionTypeRequiredDescription
apiKeystringYesYour API key from the dashboard
formSelectorstringYesCSS selector for the form
fieldSelectorstringYesCSS selector or name of the textarea
apiBaseUrlstringNoDefault: https://api.classycomments.com
onApprovefunctionNoCallback when comment is approved
validateBeforeInterceptbooleanNoCheck HTML5 validation before showing modal (default: true)
onApprovefunctionNoCallback when comment is approved
onCancelfunctionNoCallback 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 .prevent or preventDefault()
  • 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 formSelector and fieldSelector are 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: