@chargebee/code-sample-generator
v1.0.2
Published
A self-contained, reusable React component for generating and displaying API code samples from OpenAPI specifications
Maintainers
Readme
@chargebee/code-sample-generator
A self-contained, reusable React component for displaying API code samples with syntax highlighting, language switching, and copy functionality.
Features
- Display Component: Renders pre-generated code samples with beautiful syntax highlighting
- Multi-Language: Supports 12+ languages (cURL, Node.js, Python, PHP, Ruby, Java, .NET, Go, TypeScript)
- Syntax Highlighting: Powered by Shiki
- Feature-Rich: Carousel, copy button, animations, language switcher
- Browser Export: Optimized browser API for code generation (
createCodeGenerator) - Batch Generation: Efficient parallel code generation for SSG builds
- Structured Errors: Custom error classes with operation/language context
- Type Safety: Operation ID union types for autocomplete and validation
- Themeable: Configurable themes and styling
Installation
pnpm add @chargebee/code-sample-generatorReact UI consumers must also install react, react-dom, and lucide-react.
They are optional peer dependencies so headless /node consumers do not install
the React stack.
Entry Points
This package has three entry points:
| Entry Point | Import Path | Environment |
|-------------|-------------|-------------|
| Browser | @chargebee/code-sample-generator/browser | React components, hooks, constants, createCodeGenerator. Does not export highlightCode. |
| Node | @chargebee/code-sample-generator/node | React-free: generateCodeSample(s), highlightCode, descriptors. Use highlightCode at SSG/build time. |
| Main | @chargebee/code-sample-generator | Combined entry: Node generators and filesystem loaders plus the React components. |
Use /browser in client-side (React) code, and /node for headless code generation (CLIs, SSG/SSR, LLM tooling, Node/Deno/Bun). The /node entry pulls in no React or DOM dependencies. The main entry includes Node.js built-ins (node:fs, node:path) and React, so it should not be used directly in browser builds.
Build-time highlighting (highlightCode)
import { highlightCode } from '@chargebee/code-sample-generator/node';
const { html } = await highlightCode({
code: 'const site = "acme-test";',
language: 'javascript',
showLineNumbers: true,
});
// html is dual-theme: tokens carry --shiki-light and --shiki-dark.
// The host `html.dark` class (cb-docs ThemeToggle) selects which set is visible.
<CodeBlock html={html} code={source} language="javascript" />Empty, plain / var / text, and unknown fence tags resolve to Shiki plaintext (themed panel, no keyword colouring). They do not throw.
Docs tabs (languageSwitcher: "tabs")
API Docs omit this prop (default "none") so language stays in the site-header SDK dropdown. User docs pass "tabs" for author-named panes (label ≠ grammar):
<CodeSamples
features={{ languageSwitcher: "tabs", showHeader: false, showLineNumbers: true }}
samples={{
CURL: { code: `curl https://...`, language: "bash" },
JQUERY: { code: `const express = require("express");`, language: "javascript" },
}}
/>Supported Languages
| Language ID | Display Name | Notes |
|-------------|--------------|-------|
| curl | cURL | HTTP client |
| node-v2 | Node.js v2 | Legacy SDK |
| node-v3 | Node.js v3 | Current SDK |
| python-v2 | Python v2 | Legacy SDK |
| python-v3 | Python v3 | Current SDK |
| java-v3 | Java v3 | Current SDK |
| java-v4 | Java v4 | Latest SDK |
| php-v3 | PHP v3 | Legacy SDK |
| php-v4 | PHP v4 | Current SDK |
| ruby | Ruby | Current SDK |
| go | Go | Current SDK |
| dotnet | .NET | Current SDK |
Usage
Simple Usage (Code String)
import { CodeSamples } from '@chargebee/code-sample-generator/browser';
import '@chargebee/code-sample-generator/dist/code-samples.css';
function MyPage() {
return (
<CodeSamples
codeString={`curl https://your-site.chargebee.com/api/v2/customers \\
-u test_api_key: \\
-X POST \\
-d "first_name=John&last_name=Doe"`}
language="curl"
theme="github-light"
features={{
copyButton: true,
showLineNumbers: true,
}}
/>
);
}Multiple Pre-generated Samples
import { CodeSamples } from '@chargebee/code-sample-generator/browser';
import '@chargebee/code-sample-generator/dist/code-samples.css';
function MyPage() {
// Pre-generate samples using generateCodeSamples (see Pure Function API below)
const samples = {
'curl': 'curl https://...',
'node-v3': 'const chargebee = new Chargebee({...})',
'python-v3': 'import chargebee\nchargebee.configure(...)',
};
return (
<CodeSamples
samples={samples}
initialLanguage="node-v3"
theme="github-light"
features={{
carousel: true,
copyButton: true,
showLanguageSwitcher: true,
}}
/>
);
}Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| codeString | string | ❌ | Raw code string to display (simplest usage) |
| language | string | ❌ | Language identifier (required if codeString is provided) |
| samples | Record<string, string \| SampleSnippet> \| Array<{languageSnippets}> | ❌ | Pre-generated code samples. String values: key = language id (API Docs). Object values: { code, language?, label?, html? } for docs tabs. |
| responseData | any | ❌ | Sample response JSON to display |
| initialLanguage | string | ❌ | Initial language selection (default: first language) |
| theme | string \| ThemeConfig | ❌ | Shiki theme name or custom config. Use "github-dark" for dark mode, "github-light" for light mode (default: "github-dark") |
| highlighter | HighlighterConfig | ❌ | Shiki highlighter configuration |
| features | FeaturesConfig | ❌ | Feature toggles (see below) |
| className | string | ❌ | Additional CSS classes |
| style | React.CSSProperties | ❌ | Inline styles |
| replacements | Record<string, string> | ❌ | String replacements (e.g., {"{site}": "acme"}) |
| onLanguageChange | (lang: string) => void | ❌ | Language change callback |
| onCopy | (code: string) => void | ❌ | Copy callback |
| onSampleChange | (index: number) => void | ❌ | Sample change callback (for carousel) |
Features Config
| Feature | Type | Default | Description |
|---------|------|---------|-------------|
| carousel | boolean | false | Enable carousel for multiple samples |
| copyButton | boolean | true | Show copy-to-clipboard button |
| animations | boolean | true | Enable fade transitions |
| showHeader | boolean | true | Show/hide entire header |
| showLanguageSwitcher | boolean | true | Reserved; not rendered today. Prefer languageSwitcher. |
| languageSwitcher | "none" \| "dropdown" \| "tabs" | "none" | "none" = API Docs (no in-widget language chrome). "tabs" = pills above the panel. "dropdown" reserved. |
| showApiExplorer | boolean | false | Show "Try in API Explorer" button |
| showLineNumbers | boolean | true | Show line numbers in code blocks |
| title | string | "Sample Request" | Header title |
| languageFilter | string[] | undefined | Show only specific languages |
| onApiExplorerClick | () => void | undefined | Callback for API Explorer button |
API Versioning
When generating code samples, you need to specify version information:
apiVersion: Chargebee API version ("v1"or"v2")pcVersion: Product Catalog version ("v1"or"v2")- Use
"v1"for sites created before Product Catalog 2.0 - Use
"v2"for sites with Product Catalog 2.0 enabled - When in doubt, use
"v2"(default)
- Use
The combination of these versions determines which operation descriptors are used:
apiVersion="v1"→ usesv1operationsapiVersion="v2"+pcVersion="v1"→ usesv2-pcv1operationsapiVersion="v2"+pcVersion="v2"→ usesv2-pcv2operations (most common)
Theming
Dark/light mode is controlled by the theme prop:
theme="github-dark"- Dark mode styling (default)theme="github-light"- Light mode styling
Any Shiki theme name is supported. The theme affects:
- Syntax highlighting colors
- Header and button styling
- Copy button appearance
// Dark mode (default)
<CodeSamples samples={samples} theme="github-dark" />
// Light mode
<CodeSamples samples={samples} theme="github-light" />
// Custom Shiki theme
<CodeSamples samples={samples} theme="nord" />Additional Components & Hooks
For advanced usage, you can import sub-components and hooks to build custom implementations:
Sub-Components
import {
CodeBlock, // Syntax-highlighted code display
CodeSampleHeader, // Header with title and copy button
Carousel, // Multi-sample navigation
LanguageSwitcher // Language dropdown
} from '@chargebee/code-sample-generator/browser';
// Use CodeBlock for simple code display
<CodeBlock
code="const x = 1;"
language="typescript"
theme="github-light"
/>Custom Hooks
import {
useSampleLoader, // Load and normalize samples
useLanguageSelection // Manage language state
} from '@chargebee/code-sample-generator/browser';
function MyCustomComponent() {
const { samples, loading } = useSampleLoader({ /* ... */ });
const { language, setLanguage } = useLanguageSelection();
// ...
}Constants
import {
AVAILABLE_LANGUAGES, // List of supported languages
DEFAULT_LANGUAGE, // Default language ("curl")
DEFAULT_THEME // Default theme ("github-dark")
} from '@chargebee/code-sample-generator/browser';Architecture
- generators/: Language-specific code generators (Go, Node.js, Python, etc.)
- metadata/: OpenAPI metadata extraction
- components/: React UI components
- styles/: CSS themes and animations
Code Generation vs Display
Important: The CodeSamples component is a display component - it renders pre-generated code strings. It does NOT generate code from OpenAPI specs.
To generate code from OpenAPI specs, use the generateCodeSample or generateCodeSamples functions (see Pure Function API below), then pass the result to the component:
import { CodeSamples } from '@chargebee/code-sample-generator/browser';
import { generateCodeSamples } from '@chargebee/code-sample-generator/node';
// In your loader/server component (SSG/SSR)
export async function loader() {
const samples = await generateCodeSamples({
languages: ['curl', 'node-v3', 'python-v3'],
operation: 'create_a_customer',
apiVersion: 'v2',
pcVersion: 'v2',
site: 'acme-test',
apiKey: '{site_api_key}',
request: {
uri: '/customers',
method: 'POST',
params: { first_name: 'John', last_name: 'Doe', email: '[email protected]' }
}
});
return { samples };
}
// In your React component
function MyPage({ samples }) {
return <CodeSamples samples={samples} />;
}Browser Export (Client-Side Generation)
For browser environments that need to generate code dynamically (e.g., API Explorer playground), use the browser export:
Security: Never put a real Chargebee API key in browser code, generated assets, or a public documentation site. The
apiKeyvalue is inserted into the displayed sample; use a placeholder or an intentionally non-secret test value. This package generates code—it does not authenticate API requests.
Basic Setup
import { createCodeGenerator } from '@chargebee/code-sample-generator/browser';
// Create a generator instance (singleton pattern recommended)
const generator = createCodeGenerator({
apiVersion: 'v2',
pcVersion: 'v2',
site: 'acme-test',
apiKey: '{site_api_key}',
cache: 'memory' // Only 'memory' or 'none' supported in browser
});Generate Code Dynamically
// Generate code for a specific operation
const result = await generator.generate({
language: 'curl',
operation: 'create_a_customer',
request: {
params: { first_name: 'John', last_name: 'Doe', email: '[email protected]' }
}
});
console.log(result.code); // Generated code stringReact Component Example (API Explorer)
import { useState, useEffect } from 'react';
import { createCodeGenerator } from '@chargebee/code-sample-generator/browser';
function CodeSamplePlayground({ operation, params }) {
const [code, setCode] = useState('');
const [language, setLanguage] = useState('curl');
useEffect(() => {
const generator = createCodeGenerator({
apiVersion: 'v2',
pcVersion: 'v2',
site: 'acme-test',
apiKey: '{site_api_key}',
});
// Generate code when params change
generator.generate({
language,
operation,
request: { params }
}).then(result => {
setCode(result.code);
}).catch(error => {
console.error('Failed to generate code:', error);
setCode(`// Error: ${error.message}`);
});
}, [operation, params, language]);
return (
<div>
<select value={language} onChange={e => setLanguage(e.target.value)}>
<option value="curl">cURL</option>
<option value="node-v3">Node.js</option>
<option value="python-v3">Python</option>
</select>
<pre>{code}</pre>
</div>
);
}Prefetching Operations
Prefetch common operations to warm up the cache for faster subsequent generation:
const generator = createCodeGenerator({ /* ... */ });
// Prefetch operations on mount
await generator.prefetch('create_a_customer');
await generator.prefetch('list_customers');
await generator.prefetch('update_a_customer');Runtime Validation
Validate operation IDs at runtime before generating code:
import { validateOperationId, type OperationId } from '@chargebee/code-sample-generator/browser';
// Option 1: Type-safe from the start (if you know the operation)
const operation: OperationId = 'create_a_customer';
const result = await generator.generate<'create_a_customer'>({
language: 'curl',
operation, // ✅ Type-safe OperationId
request: {
params: {
first_name: 'John', // ✅ Type-safe (if OperationParamsMap is extended)
email: '[email protected]'
}
}
});
// Option 2: Runtime validation for user input
const userInput: string = getUserInput(); // From form, API, etc.
if (validateOperationId(userInput)) {
// TypeScript now knows userInput is OperationId
const result = await generator.generate({
language: 'curl',
operation: userInput, // ✅ Type-safe (validated OperationId)
request: {
params: {
first_name: 'John',
email: '[email protected]'
}
}
});
} else {
console.error('Invalid operation ID:', userInput);
}Note: For full type safety of request.params, you need to:
- Use the generic type parameter:
generator.generate<'create_a_customer'>() - Extend
OperationParamsMapvia module augmentation (see Pure Function API section)
Error Handling
The browser API throws structured errors:
import {
CodeGenerationError,
OperationNotFoundError,
LanguageNotSupportedError
} from '@chargebee/code-sample-generator/browser';
try {
const result = await generator.generate({ /* ... */ });
} catch (error) {
if (error instanceof OperationNotFoundError) {
console.error(`Operation not found: ${error.operation} for version ${error.version}`);
} else if (error instanceof LanguageNotSupportedError) {
console.error(`Language not supported: ${error.language}`);
console.log(`Supported: ${error.supportedLanguages.join(', ')}`);
} else if (error instanceof CodeGenerationError) {
console.error(`Failed to generate ${error.language} code for ${error.operation}`);
if (error.cause) {
console.error('Cause:', error.cause);
}
}
}Note: Pass openapi or a pre-extracted descriptor into createCodeGenerator / generate. Memory caching is optional.
Pure Function API (Server-Side)
For Node.js, LLMs, Deno, Bun, or server-side generation (SSG/SSR), use the pure function API:
import { generateCodeSample, generateCodeSamples } from '@chargebee/code-sample-generator/node';
// Generate code for a specific language (pass openapi or descriptor)
const result = await generateCodeSample({
language: 'node-v3',
operation: 'create_a_customer',
apiVersion: 'v2',
pcVersion: 'v2',
site: 'acme-test',
apiKey: '{site_api_key}',
openapi, // host-supplied OpenAPI document
request: {
uri: '/customers',
method: 'POST',
params: { first_name: 'John', last_name: 'Doe', email: '[email protected]' }
}
});
console.log(result.code); // Generated Node.js code string
console.log(result.language); // 'node-v3'
// Generate code for multiple languages
const allSamples = await generateCodeSamples({
languages: ['curl', 'node-v3', 'python-v3', 'java-v3'],
operation: 'create_a_customer',
apiVersion: 'v2',
pcVersion: 'v2',
site: 'acme-test',
apiKey: '{site_api_key}',
openapi,
request: {
uri: '/customers',
method: 'POST',
params: { first_name: 'John', email: '[email protected]' }
}
});
console.log(allSamples['node-v3']); // Node.js code
console.log(allSamples['python-v3']); // Python code
// Or use pre-generated samples (no generation needed)
const pregenResult = await generateCodeSample({
language: 'python-v3',
samples: {
'python-v3': 'import chargebee\nchargebee.configure(...)'
}
});Use Cases:
- LLM/AI code generation tools
- Server-side code generation (SSG/SSR)
- CLI tools and scripts
- Non-React applications
- Testing and validation
Advanced Exports
Code Generators (Server-Side)
Generators require an OpenAPI document or a pre-extracted descriptor:
import {
getGenerator,
getSupportedLanguages,
getSpecDescriptors,
generateCodeSample,
} from '@chargebee/code-sample-generator/node';
// Get list of supported languages
const languages = getSupportedLanguages(); // ['curl', 'node-v3', 'python-v3', ...]
// Get generator for a specific language
const generator = getGenerator('node-v3');
// Resolve descriptors from your OpenAPI document
const descriptors = getSpecDescriptors(openapi);
const operation = descriptors.get('create_a_customer');
// Or pass openapi/descriptor into generateCodeSample
await generateCodeSample({ operation: 'create_a_customer', language: 'curl', openapi, /* ... */ });Type Exports
import type {
CodeSamplesPropsInterface,
GenerateCodeSampleOptions,
GenerateCodeSampleResult,
Language,
ApiVersion,
OperationDescriptor,
OperationId, // Union type for all operation IDs
V1OperationId, // v1 operation IDs
V2Pcv1OperationId, // v2-pcv1 operation IDs
V2Pcv2OperationId // v2-pcv2 operation IDs (most common)
} from '@chargebee/code-sample-generator/node';
// Error classes
import {
CodeGenerationError,
OperationNotFoundError,
LanguageNotSupportedError
} from '@chargebee/code-sample-generator/node';Helper Components
import {
GoToApiExplorer, // Link to API Explorer
TryInApiExplorer // Button to try in API Explorer
} from '@chargebee/code-sample-generator/browser';Placeholder Replacement
The code samples package supports placeholder replacement for site names and API keys. This is useful for:
- Build-time replacement: Replace placeholders during SSG build
- Runtime replacement: Replace placeholders in browser/client-side code
How Placeholders Work
By default, generated code includes placeholders like {site} and {site_api_key}:
curl https://{site}.chargebee.com/api/v2/customers \
-u {site_api_key}:Build-Time Replacement (SSG)
In your loader/server component, generate code with placeholders, then replace them:
import { generateCodeSamples } from '@chargebee/code-sample-generator/node';
// Generate with placeholders
const samples = await generateCodeSamples({
languages: ['curl', 'node-v3'],
operation: 'create_a_customer',
site: '{site}', // Placeholder
apiKey: '{site_api_key}', // Placeholder
// ...
});
// Replace at build time if needed
const replacedSamples = Object.fromEntries(
Object.entries(samples).map(([lang, code]) => [
lang,
code.replace(/{site}/g, 'acme-test').replace(/{site_api_key}/g, 'test_key')
])
);Runtime Replacement (Component)
Use the replacements prop in the CodeSamples component:
<CodeSamples
samples={samples}
replacements={{
'{site}': 'acme-test',
'{site_api_key}': 'test_api_key'
}}
/>Common Placeholders
| Placeholder | Description | Default Value |
|-------------|-------------|---------------|
| {site} | Chargebee site name | "your-site" |
| {site_api_key} | API key | "test_api_key" |
When to Use Replacements
- Keep placeholders: When you want users to replace them manually (documentation)
- Replace at build time: When generating static docs with example values
- Replace at runtime: Only with a placeholder or intentionally non-secret test value; never expose a real API key in browser code
Batch Generation API (SSG)
For efficient code generation during SSG builds, use the batch API:
import { generateCodeSamplesBatch } from '@chargebee/code-sample-generator/node';
// Generate code for multiple samples and languages in parallel
const results = await generateCodeSamplesBatch({
samples: [
{
name: "Create with card",
sample: {
uri: "/customers",
method: "POST",
params: { first_name: "John" }
}
},
{
name: "Create with token",
sample: {
uri: "/customers",
method: "POST",
params: { payment_method: { type: "card" } }
}
}
],
languages: ["curl", "node-v3", "python-v3", "java-v3"],
config: {
apiVersion: "v2",
pcVersion: "v2",
operation: "create_a_customer",
site: "{site}",
apiKey: "{site_api_key}"
}
});
// Results format matches API docs loader expectations
results.forEach(result => {
console.log(result.displayName); // "Create with card"
console.log(result.languageSnippets); // { curl: "...", node-v3: "...", ... }
});This is more efficient than generating code sequentially, especially when generating for many languages and sample variations.
License
Use of this package is governed by an End User License Agreement. The source code remains the proprietary property of Chargebee Inc. See LICENSE for the full terms.
Report vulnerabilities privately as described in SECURITY.md.
