A lightweight (< 450B) yet powerful utility for dynamic class name generation in JavaScript applications. Perfect for React, React Native, Vue.js, and any JavaScript project where you need flexible class name handling with type safety.
class-glue solves the common challenge of managing dynamic class names in modern web applications. Whether you're toggling classes based on state, merging CSS modules, or handling complex conditional styling, class-glue provides an elegant and performant solution.
- πͺΆ Lightweight: Entire core is just 425B (minified + gzipped), with modular utilities each under 540B
- π Type-Safe: Built with TypeScript for robust type checking and excellent IDE support
- π Framework Agnostic: Works seamlessly with React, React Native/Expo, Vue, Angular, or vanilla JavaScript
- 𧩠Modular Design: Import only what you need - each utility can be used standalone
- π― Zero Dependencies: No external dependencies means no bloat in your project
- π³ Tree-Shakeable: Modern bundlers can eliminate unused code for optimal bundle size
- β‘ Performance Focused: Optimized internals with minimal runtime overhead
- Main API (
class-glue
): Full-featured class name generation - String Only (
join-strings
): Optimized for simple string concatenation - Object Keys (
keys-to-strings
): Efficient object-based class generation - CSS Modules (
merge-module-strings
): First-class CSS Modules support - Style Objects (
merge-styles
): React Native and style object handling
- Full TypeScript support with detailed type definitions
- Intelligent type inference for better development experience
- Strict type checking to catch errors early
- Comprehensive source maps for easy debugging
- Detailed error messages in development
- Extensive documentation with real-world examples
- Thoroughly tested with 100% coverage
- Used in production by numerous projects
- Active maintenance and community support
import classGlue from 'class-glue';
classGlue('btn', { active: true }, ['primary']);
// => 'btn active primary'
import classGlueString from 'class-glue/join-strings';
classGlueString('card', isActive && 'card-active');
// => 'card card-active' (if isActive is true)
import keysToStrings from 'class-glue/keys-to-strings';
keysToStrings({
btn: true,
'btn-active': isActive
});
// => 'btn btn-active' (if isActive is true)
import createModuleGlue from 'class-glue/merge-module-strings';
import styles from './Button.module.css';
const moduleGlue = createModuleGlue(styles);
moduleGlue('button', { active: isActive });
// => 'Button_button_x7d Button_active_d4f'
import createStyleGlue from 'class-glue/merge-styles';
const styleGlue = createStyleGlue({
base: { padding: 10 },
active: { backgroundColor: 'blue' }
});
styleGlue('base', { active: isActive });
// => { padding: 10, backgroundColor: 'blue' }
npm install class-glue
yarn add class-glue
pnpm add class-glue
bun add class-glue
<!-- Global: window.classG -->
<script src="https://unpkg.com/class-glue"></script>
<!-- Individual utilities -->
<script src="https://unpkg.com/class-glue/dist/umd/join-strings.min.js"></script> <!-- window.classGJoinStrings -->
<script src="https://unpkg.com/class-glue/dist/umd/keys-to-strings.min.js"></script> <!-- window.classGKeysToStrings -->
<script src="https://unpkg.com/class-glue/dist/umd/merge-module-strings.min.js"></script> <!-- window.classGMergeModuleStrings -->
<script src="https://unpkg.com/class-glue/dist/umd/merge-styles.min.js"></script> <!-- window.classGMergeStyles -->
When using via CDN, the utilities are available as:
window.classG
(main)window.classGJoinStrings
window.classGKeysToStrings
window.classGMergeModuleStrings
window.classGMergeStyles
class-glue ships with multiple build formats:
Format | Entry Point | Usage |
---|---|---|
CommonJS | dist/index.js |
Node.js, Webpack, etc. |
ES Module | dist/esm/index.js |
Modern bundlers |
UMD | dist/umd/index.min.js |
Direct browser usage |
Each utility is available in all formats above. Import the one that best suits your needs:
// ES Modules (Recommended)
import classGlue from 'class-glue';
// CommonJS
const classGlue = require('class-glue');
// Individual utilities
import joinStrings from 'class-glue/join-strings';
import keysToStrings from 'class-glue/keys-to-strings';
import createModuleGlue from 'class-glue/merge-module-strings';
import createStyleGlue from 'class-glue/merge-styles';
import classGlue from 'class-glue';
// Simple strings
classGlue('btn', 'primary'); // => 'btn primary'
// Conditionals
classGlue('btn', {
'btn-primary': true,
'btn-disabled': false
}); // => 'btn btn-primary'
// Arrays
classGlue('btn', ['primary', 'disabled']); // => 'btn primary disabled'
// Ignored values
classGlue('btn', undefined, null, false); // => 'btn'
// Mixed values (strings, objects, arrays, etc.)
classGlue('btn', 'primary', { active: true }, ['disabled']); // => 'btn primary active disabled'
import classGlue from 'class-glue';
function Button({ isActive, isDisabled, size, children }) {
return (
<button
className={classGlue(
'btn',
{
'btn-active': isActive,
'btn-disabled': isDisabled,
},
size && `btn-${size}`
)}
>
{children}
</button>
);
}
import createModuleGlue from 'class-glue/merge-module-strings';
import styles from './Card.module.css';
const clgl = createModuleGlue(styles);
function Card({ isHighlighted }) {
return (
<div className={clgl('card', { cardHighlighted: isHighlighted })}>
{/* Resolves to actual CSS Module classes like "Card_card_x7d Card_cardHighlighted_f3j" */}
</div>
);
}
import createStyleGlue from 'class-glue/merge-styles';
const styles = {
container: { padding: 16, borderRadius: 8 },
active: { backgroundColor: 'blue' },
disabled: { opacity: 0.5 }
};
const clgl = createStyleGlue(styles);
function Card({ isActive, isDisabled }) {
return (
<View style={clgl('container', {
active: isActive,
disabled: isDisabled
})}>
{/* Merges styles based on conditions */}
</View>
);
}
import keysToStrings from 'class-glue/keys-to-strings';
// Object syntax for multiple conditions
keysToStrings({
base: true, // always included
active: isActive, // included if isActive is true
disabled: isDisabled, // included if isDisabled is true
[`size-${size}`]: size, // included if size is truthy
[styles.custom]: hasCustom // CSS Module class names work too
});
import classGlue from 'class-glue';
import type { ClassValue } from 'class-glue';
// All utilities are fully typed
function Button<T extends string>({ className, variant }: {
className?: ClassValue;
variant?: T;
}) {
return (
<button
className={classGlue(
'btn',
className, // safely accepts ClassValue type
variant && `btn-${variant}`
)}
/>
);
}
import classGlue from 'class-glue';
classGlue(...args: ClassValue[]): string;
type ClassValue =
| string
| number
| boolean
| undefined
| null
| { [key: string]: boolean | undefined | null }
| ClassValue[];
// Full featured class name generation
classGlue(
'string', // plain strings
{ active: true }, // object keys with boolean values
['array', 'of', 'classes'], // arrays
undefined, // ignored
null, // ignored
false // ignored
);
import joinStrings from 'class-glue/join-strings';
joinStrings(...args: Array<string | undefined | null | boolean>): string;
// Optimized for string-only operations
joinStrings(
'button',
isActive && 'active', // conditional strings
undefined, // ignored
null, // ignored
isPrimary && 'primary'
);
import keysToStrings from 'class-glue/keys-to-strings';
keysToStrings(obj: { [key: string]: boolean | undefined | null }): string;
// Efficient object-based class generation
keysToStrings({
btn: true, // always included
'btn-primary': isPrimary, // included if isPrimary is true
'btn-active': isActive, // included if isActive is true
'btn-disabled': false // never included
});
import createModuleGlue from 'class-glue/merge-module-strings';
import type { ClassValue } from 'class-glue';
createModuleGlue(styleModule: { [key: string]: string }):
(...args: ClassValue[]) => string;
// CSS Modules integration
import styles from './Button.module.css';
const clgl = createModuleGlue(styles);
clgl(
'button', // looks up 'button' in styles object
{ active: true }, // looks up 'active' in styles object
styles.custom // used as-is
);
import createStyleGlue from 'class-glue/merge-styles';
import type { ClassValue } from 'class-glue';
createStyleGlue(styles: { [key: string]: any }):
(...args: ClassValue[]) => { [key: string]: any };
// Style object merging (React Native)
const styles = {
base: { padding: 16 },
active: { backgroundColor: 'blue' }
};
const clgl = createStyleGlue(styles);
clgl(
'base', // includes base styles
{ active: true }, // merges active styles
customStyles // merges custom styles
);
Each utility is designed for specific use cases:
class-glue
: Full-featured, handles all typesjoin-strings
: Optimized for string-only operationskeys-to-strings
: Efficient object-based class generationmerge-module-strings
: CSS Modules integrationmerge-styles
: Style object merging (React Native)
Contributions are always welcome! Please read the contribution guidelines before getting started.
If you encounter any issues while using class-glue, please open an issue on the GitHub repository. Make sure to include a clear description, steps to reproduce the issue, and any relevant code snippets or error messages.
Rahul Shetty
Lead Engineer & Open Source Enthusiast
|
If you find class-glue useful, please consider:
- Starring the repository β
- Supporting the project π
- Contributing to the project π€
- Sharing your feedback and ideas π
- Referring to the project in your blog posts or articles π
- Sharing the project with your friends and colleagues π«
Thank you for your support! π
class-glue is inspired by clsx, but with added features for TypeScript, CSS Modules, and React Native styles.
MIT Β© Rahul Shetty