React Integration
XKin provides a lightweight styling solution for React applications that avoids the overhead of larger CSS-in-JS libraries. This guide demonstrates how to integrate XKin's component system with React to create flexible, themed components.
Overview
React is the most popular JavaScript library for building user interfaces. XKin enhances React applications with:
- Component-based styling with minimal overhead
- Efficient theme management
- Dynamic styling based on props and state
- No runtime CSS injection (styles are pre-calculated)
- Excellent performance for both SSR and CSR apps
Basic Component Example
Here's a simple button component using XKin for styling:
import React, { useState } from "react";
/**
* Button component styling definition
*/
const ButtonStyle = xkin.component({
base: {
class: "button",
style: "border: none; border-radius: 4px; cursor: pointer; transition: all 0.2s;"
},
setup: ({ size, disabled, height }) => ({
class: {
"button-sm": size === "sm",
"button-lg": size === "lg",
"button-disabled": disabled
},
style: {
height: height || "auto",
opacity: disabled ? 0.7 : 1,
pointerEvents: disabled ? "none" : "auto",
padding: size === "sm" ? "6px 12px" : size === "lg" ? "12px 24px" : "8px 16px",
fontSize: size === "sm" ? "0.875rem" : size === "lg" ? "1.125rem" : "1rem"
}
}),
theme: {
primary: {
class: "color-bg-primary color-tx-white",
style: "box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"
},
secondary: {
class: "color-bg-secondary color-tx-white",
style: "box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"
},
danger: {
class: "color-bg-danger color-tx-white",
style: "box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"
}
}
});
/**
* Button component that uses XKin for styling
*/
export function Button(props) {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
if (props.onClick) props.onClick();
};
// Generate styles from props
const styles = ButtonStyle(props).theme(props.mode);
return (
<button
type={props.type || "button"}
onClick={handleClick}
className={styles.class}
style={styles.style}
disabled={props.disabled}
>
{props.children || `Count: ${count}`}
</button>
);
}
Using the Component
import React, { useState, useEffect } from "react";
import { Button } from "./components/Button";
export function App() {
// State for component props
const [disabled, setDisabled] = useState(false);
const [mode, setMode] = useState("primary");
// Toggle disabled state every 5 seconds
useEffect(() => {
const disabledInterval = setInterval(() => {
setDisabled(prev => !prev);
}, 5000);
// Clean up on unmount
return () => clearInterval(disabledInterval);
}, []);
// Cycle through themes every second
useEffect(() => {
const themes = ["primary", "secondary", "danger", null];
let currentIndex = 0;
const themeInterval = setInterval(() => {
currentIndex = (currentIndex + 1) % themes.length;
setMode(themes[currentIndex]);
}, 1000);
return () => clearInterval(themeInterval);
}, []);
return (
<div className="container">
<h1>XKin + React Demo</h1>
<div className="button-container">
<Button
size="sm"
mode={mode}
disabled={disabled}
onClick={() => console.log("Button clicked!")}
>
Small Button
</Button>
<Button mode={mode} disabled={disabled}>
Default Button
</Button>
<Button size="lg" mode={mode} disabled={disabled}>
Large Button
</Button>
</div>
<div className="status">
<p>Current Theme: <strong>{mode || "none"}</strong></p>
<p>Disabled: <strong>{disabled ? "Yes" : "No"}</strong></p>
</div>
</div>
);
}
Advanced Patterns
Creating a Higher-Order Component
You can create a higher-order component (HOC) to apply XKin styling to any component:
import React from "react";
/**
* HOC that applies XKin styling to any component
*/
export function withXkinStyle(Component, styleConfig) {
// Create the XKin component once
const xkinComponent = xkin.component(styleConfig);
// Return a new component that applies the styling
return function StyledComponent(props) {
// Generate styles from props
const styles = xkinComponent(props).theme(props.theme);
// Apply styles to the wrapped component
return <Component {...props} className={styles.class} style={styles.style} />;
};
}
// Usage example
const styleConfig = {
base: { class: "card", style: "border-radius: 8px;" },
setup: ({ elevated }) => ({
style: elevated ? "box-shadow: 0 8px 16px rgba(0,0,0,0.1);" : ""
}),
theme: {
dark: { class: "color-bg-dark color-tx-white" }
}
};
// Create a styled div
const Card = withXkinStyle("div", styleConfig);
// Use the styled component
function MyComponent() {
return (
<Card elevated theme="dark">
<h2>Card Title</h2>
<p>Card content goes here</p>
</Card>
);
}
Creating a Custom Hook
For more flexibility, you can create a custom hook to use XKin styling:
import React, { useMemo } from "react";
/**
* Custom hook for XKin styling
*/
function useXkinStyle(styleConfig, props) {
// Memoize the component to prevent recreation
const component = useMemo(() => xkin.component(styleConfig), []);
// Generate and memoize styles
const styles = useMemo(() => {
return component(props).theme(props.theme);
}, [props, props.theme, component]);
return styles;
}
// Usage in a component
function Card({ children, elevated, theme }) {
const styleConfig = {
base: { class: "card", style: "border-radius: 8px;" },
setup: ({ elevated }) => ({
style: elevated ? "box-shadow: 0 8px 16px rgba(0,0,0,0.1);" : ""
}),
theme: {
dark: { class: "color-bg-dark color-tx-white" }
}
};
const styles = useXkinStyle(styleConfig, { elevated, theme });
return (
<div className={styles.class} style={styles.style}>
{children}
</div>
);
}
Creating a Theme Context
React's Context API can be used to manage themes across your application:
import React, { createContext, useContext, useState, useMemo } from "react";
// Create a context for theme management
const ThemeContext = createContext();
// Theme provider component
export function ThemeProvider({ children, initialTheme = "light" }) {
const [theme, setTheme] = useState(initialTheme);
// Memoize the context value to prevent unnecessary renders
const contextValue = useMemo(() => ({
theme,
setTheme,
toggleTheme: () => setTheme(current => current === "light" ? "dark" : "light")
}), [theme]);
return (
<ThemeContext.Provider value={contextValue}>
{children}
</ThemeContext.Provider>
);
}
// Custom hook to use the theme
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}
// Usage in a component
function ThemedButton({ children, ...props }) {
const { theme, toggleTheme } = useTheme();
const buttonStyle = xkin.component({
// Style definition...
});
const styles = buttonStyle(props).theme(theme);
return (
<button
className={styles.class}
style={styles.style}
onClick={toggleTheme}
>
{children}
</button>
);
}
// App with theme provider
function App() {
return (
<ThemeProvider initialTheme="light">
<div className="app">
<ThemedButton>Toggle Theme</ThemedButton>
</div>
</ThemeProvider>
);
}
Performance Optimization
React applications can benefit from several XKin optimization techniques:
- Define component styles outside of the render function to prevent recreation
- Use
React.memo
for components that use XKin styling - Use
useMemo
to memoize style generation - Use
xkin.memoizeOne
for complex style calculations - For lists, define style configurations outside the component
import React, { useMemo, memo } from "react";
// Define once outside any component
const ListItemStyle = xkin.component({
base: { class: "list-item" },
setup: xkin.memoizeOne(props => ({
class: {
"list-item-active": props.active,
"list-item-selected": props.selected
}
}))
});
// Memoized list item component
const ListItem = memo(function ListItem({ item }) {
const styles = ListItemStyle({
active: item.isActive,
selected: item.isSelected
});
return (
<li className={styles.class}>
{item.text}
</li>
);
});
// List component with optimized rendering
function ListView({ items }) {
return (
<ul>
{items.map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
}
Server-Side Rendering (SSR)
XKin works well with server-side rendering in Next.js or other React SSR frameworks:
// pages/index.js in a Next.js app
import React from "react";
import { Button } from "../components/Button";
// This component will be rendered on the server first
export default function HomePage() {
return (
<div className="container">
<h1>Welcome to XKin + Next.js</h1>
<Button
size="lg"
mode="primary"
>
Get Started
</Button>
</div>
);
}
Since XKin generates static class names and styles without runtime injection, it works seamlessly with SSR without hydration mismatches.
Integration with Other React Libraries
React Router
import React from "react";
import { Link } from "react-router-dom";
const LinkStyle = xkin.component({
base: {
class: "nav-link",
style: "text-decoration: none; padding: 8px 16px;"
},
setup: ({ active }) => ({
class: {
"nav-link-active": active
},
style: {
fontWeight: active ? "bold" : "normal"
}
})
});
// Custom link component with XKin styling
function StyledLink({ to, children, ...props }) {
// Check if this link is active
const active = window.location.pathname === to;
// Generate styles
const styles = LinkStyle({ active }).theme(props.theme);
return (
<Link
to={to}
className={styles.class}
style={styles.style}
>
{children}
</Link>
);
}
React Hook Form
import React from "react";
import { useForm } from "react-hook-form";
// Form input styling
const InputStyle = xkin.component({
base: {
class: "form-input",
style: "padding: 8px; border: 1px solid #ccc; border-radius: 4px; width: 100%;"
},
setup: ({ error }) => ({
class: {
"form-input-error": error
},
style: {
borderColor: error ? "#dc3545" : undefined
}
})
});
function SignupForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<label>Email</label>
<input
{...register("email", { required: true, pattern: /^\S+@\S+$/i })}
className={InputStyle({ error: errors.email }).class}
style={InputStyle({ error: errors.email }).style}
/>
{errors.email && <span className="error">Valid email is required</span>}
</div>
<button type="submit">Submit</button>
</form>
);
}