# Performance Optimization Standards for Emotion
This document outlines the performance optimization standards for Emotion, a popular CSS-in-JS library. It focuses on techniques to enhance application speed, responsiveness, and resource utilization when using Emotion. These standards are designed to guide developers and AI coding assistants in producing efficient and maintainable Emotion code.
## 1. Understanding Emotion's Performance Characteristics
Before diving into specific standards, it's crucial to understand how Emotion works under the hood and where performance bottlenecks might arise.
* **Compilation:** Emotion compiles CSS styles at build time or runtime. Runtime compilation can impact initial load time and performance, so precompilation is preferred where possible.
* **CSS Injection:** Emotion injects CSS into the "" of the document. Excessive injection can lead to style recalculations by the browser, hurting performance.
* **Dynamic Styles:** While powerful, dynamic styles (styles based on props or state) can trigger re-renders and style updates more frequently.
* **Specificity:** Emotion uses a predictable class name generation scheme which helps manage CSS specificity. However, complex style overrides can still lead to performance problems.
* **SSR (Server-Side Rendering):** Proper setup is critical to avoid FOUC (Flash Of Unstyled Content) and wasted client-side processing.
## 2. Minimizing CSS Compilation and Injection Overhead
Reducing the amount of CSS that needs to be compiled and injected is fundamental to optimizing Emotion's performance.
### 2.1. Do This: Utilize Static CSS as Much as Possible
**Standard:** Favor static CSS classes for styles that don't change based on component props or application state.
**Why:** Static CSS is compiled once and reused, avoiding runtime compilation costs and reducing CSS injection overhead.
"""jsx
// Do This:
import styled from '@emotion/styled';
const Button = styled.button"
/* Static styles */
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
/* Dynamic styles (if needed) */
${props => props.primary && "
background-color: #008CBA;
"}
";
function MyComponent() {
return Click Me;
}
"""
**Don't Do This:**
"""jsx
// Don't Do This: Using dynamic styles for everything, including static styles
import styled from '@emotion/styled';
const Button = styled.button"
background-color: ${props => props.primary ? '#008CBA' : '#4CAF50'};
border: none;
color: white;
padding: ${props => '15px 32px'}; // Unnecessary dynamic style
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
";
function MyComponent() {
return Click Me;
}
"""
**Explanation:** In the "Don't Do This" example, even static values like "padding" are unnecessarily wrapped in a dynamic function. This forces Emotion to re-evaluate the style on every render, even though the value never changes. By using a mix of static and dynamic styling as shown in "Do This," static css is only compiled once and used on every render.
### 2.2. Do This: Leverage Emotion's "css" Prop for One-Off Styles
**Standard:** Use the "css" prop for applying unique, component-specific styles, especially within functional components.
**Why:** The "css" prop provides a lightweight way to apply styles without creating a new styled component, avoiding unnecessary component instances. It also can benefit from automatic vendor prefixing and other Emotion features.
"""jsx
// Do This:
import React from 'react';
import { css } from '@emotion/react';
function MyComponent() {
return (
This is my component.
);
}
"""
**Don't Do This:** Creating a new styled component for a single instance of styling:
"""jsx
import styled from '@emotion/styled';
const MyDiv = styled.div"
background-color: lightblue;
padding: 10px;
border-radius: 5px;
";
function MyComponent() {
return This is my component.;
}
"""
**Explanation:** When dealing with isolated styling needs that don't logically belong to a reusable component, the "css" prop offers a leaner approach. Creating "MyDiv" introduces an unnecessary component abstraction for styling that is specific to this one element.
### 2.3. Do This: Memoize Dynamic Styles
**Standard:** Utilize "React.memo", "useMemo", or similar techniques to prevent unnecessary re-renders when dynamic styles are based on props that haven't changed.
**Why:** Re-rendering components and recalculating styles when props haven't changed is a waste of resources. Memoization ensures that styles are only recalculated when necessary.
"""jsx
// Do This: Memoize the component
import React, { useMemo } from 'react';
import styled from '@emotion/styled';
const StyledDiv = styled.div"
padding: 10px;
background-color: ${props => props.bgColor};
";
const MyComponent = React.memo(function({ bgColor }) {
console.log("Component rendered!"); // Verify it's only rendering when bgColor changes
return Hello;
});
function App() {
const [color, setColor] = React.useState('red');
return (
setColor('blue')}>Change Color
setColor('blue')}>Change Color (Again)
);
}
"""
**Don't Do This:** Forgetting to memoize when using dynamic styles:
"""jsx
// Don't Do This: No memoization, re-renders on *every* parent render
import React from 'react';
import styled from '@emotion/styled';
const StyledDiv = styled.div"
padding: 10px;
background-color: ${props => props.bgColor};
";
function MyComponent({ bgColor }) {
console.log("Component rendered!"); // Renders every time the parent renders
return Hello;
}
function App() {
const [color, setColor] = React.useState('red');
const [count, setCount] = React.useState(0);
return (
setColor('blue')}>Change Color
setCount(count + 1)}>Increment Count
<p>Count: {count}</p>
);
}
"""
**Explanation:** In the "Don't Do This" example, "MyComponent" re-renders every time the parent component (App) re-renders, even if "bgColor" prop hasn't changed. This is extremely wasteful. By using "React.memo" in "Do This", the component only re-renders if the "bgColor" prop *actually* changes.
### 2.4 Do This: Optimize Theme Usage
**Standard:** Access the theme object efficiently and minimize unnecessary re-renders triggered by theme updates.
**Why:** Frequent theme updates can cause performance issues if not handled carefully. Theme changes should only trigger necessary component re-renders.
"""jsx
//Do This: Using useTheme effectively
import React, { useMemo } from 'react';
import { useTheme } from '@emotion/react';
const MyComponent = React.memo(function() {
const theme = useTheme();
//Memoizing the styles to prevent unnecessary recalculations.
const styles = useMemo(() => ({
backgroundColor: theme.colors.primary,
color: theme.colors.text,
padding: theme.spacing.medium,
}), [theme]); //Dependencies array includes the theme so it only updates when the theme prop has changed.
return (
Themed Content
);
});
export default MyComponent;
"""
**Don't Do This:** Inefficient use of "useTheme" hook without memoization.
"""jsx
// Don't Do This: Inefficient use of useTheme hook without memoization
import React from 'react';
import { useTheme } from '@emotion/react';
const MyComponent = () => {
const theme = useTheme();
const styles = {
backgroundColor: theme.colors.primary,
color: theme.colors.text,
padding: theme.spacing.medium,
};
return (
Themed Content
);
};
export default MyComponent;
"""
**Explanation:** In the "Don't Do This", the styles object is recreated on every render, even if the theme hasn't changed. This forces unnecessary re-renders of child components. The corrected "Do This" example utilizes the "useMemo" hook to memoize the style object, preventing re-creation unless the theme object itself changes. This drastically reduces unnecessary re-renders, especially if your theme changes frequently.
## 3. CSS Optimization Techniques
Beyond optimizing Emotion's core mechanisms, standard CSS optimization techniques are also relevant.
### 3.1. Do This: Avoid Deeply Nested Selectors
**Standard:** Keep CSS selectors as shallow and specific as possible. Especially avoid nesting selectors within styled components or the "css" prop, unless they are absolutely necessary.
**Why:** Deeply nested selectors are more expensive for the browser to match and can contribute to performance slowdowns.
"""jsx
//Do This: Using flat selectors
import styled from '@emotion/styled';
const Container = styled.div"
.title {
font-size: 20px;
}
";
"""
**Don't Do This:** Deeply nested selectors.
"""jsx
//Don't Do This: Deeply nested selectors
import styled from '@emotion/styled';
const Container = styled.div"
div > div > .title {
font-size: 20px;
}
";
"""
**Explanation:** The "Don't Do This" example uses a very specific and deeply nested selector which will be slower to resolve than than the "Do This" example.
### 3.2. Do This: Leverage CSS Variables (Custom Properties)
**Standard:** Use CSS variables for theming and reusable style values.
**Why:** CSS variables allow you to change values in a single place, reducing the amount of CSS that needs to be re-calculated and injected.
"""jsx
//Do This: Using CSS Variables
import { css } from '@emotion/react';
const globalStyles = css"
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
}
";
const buttonStyles = css"
background-color: var(--primary-color);
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
&:hover {
background-color: var(--secondary-color);
}
";
"""
**Don't Do This:** Hardcoding style values or duplicating them:
"""jsx
//Don't Do This : Hardcoding and duplicating values
import { css } from '@emotion/react';
const buttonStyles = css"
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
&:hover {
background-color: #6c757d;
}
";
"""
**Explanation:** In the "Don't Do This," the primary and secondary colors are hardcoded directly into the CSS. If you want to change these colors, you'd have to update every instance where they're used. Using CSS variables in the "Do This" example centralizes these values. Changing the variables updates all components using them automatically.
### 3.3. Do This: Optimize Images and Assets
**Standard:** Optimize images (compress, use appropriate formats like WebP), use lazy loading, and leverage CDNs for asset delivery.
**Why:** Large, unoptimized images are a common performance bottleneck. Lazy loading prevents unnecessary loading of images that aren't initially visible, and CDNs provide faster content delivery.
**Example:** This isn't specific to Emotion but is critical for overall performance. Use tools like "imagemin" or online optimizers to compress images before including them in your project. Implement lazy loading using the "loading="lazy"" attribute on "" tags.
## 4. Server-Side Rendering (SSR) Optimization
SSR is crucial for improving initial load time and SEO. Emotion provides specific tools for SSR.
### 4.1. Do This: Use Emotion's "CacheProvider" and "extractCritical" Properly for SSR
**Standard:** Use Emotion's SSR utilities to extract critical CSS and inject it into the initial HTML.
**Why:** This avoids FOUC and ensures that the page is styled correctly on the first render.
"""jsx
// Example (simplified): SSR setup in Next.js
// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { extractCritical } from '@emotion/server';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
const critical = extractCritical(initialProps.html);
initialProps.styles = (
<>
{initialProps.styles}
danielsogl
Created Mar 6, 2025
# Component Design Standards for Emotion
This document outlines the component design standards for Emotion, focusing on creating reusable, maintainable, and performant components within the Emotion ecosystem. It serves as a guide for developers and provides context for AI coding assistants.
## 1. Component Architecture
### 1.1. Atomic Design Principles
**Standard:** Embrace Atomic Design principles to create a scalable and maintainable component library.
* **Why:** Atomic Design provides a structured approach to component creation, promoting reusability, consistency, and easier maintenance. It breaks down the UI into fundamental building blocks that can be combined to create more complex components.
* **Do This:**
* Identify and create Atoms (smallest indivisible elements like buttons, inputs, labels).
* Combine Atoms into Molecules (simple groups of UI elements functioning as a unit, e.g., a search bar consisting of an input and a button).
* Assemble Molecules into Organisms (relatively complex sections of the UI, e.g., a product listing with image, title, and price).
* Compose Organisms into Templates (page-level layouts defining underlying content structure).
* Utilize Templates to construct Pages (specific instances of templates with representative content).
* **Don't Do This:**
* Create monolithic components that handle too much logic or are difficult to reuse.
* Skip the atomic design process and directly create complex components, leading to inconsistencies and maintenance difficulties.
* **Code Example:**
"""jsx
// Atom: Button
import styled from '@emotion/styled';
const Button = styled.button"
background-color: #4CAF50;
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
border-radius: 5px;
&:hover {
background-color: #3e8e41;
}
";
export default Button;
// Molecule: Search Bar
import React from 'react';
import styled from '@emotion/styled';
import Button from './Button';
const SearchBarContainer = styled.div"
display: flex;
align-items: center;
";
const Input = styled.input"
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
margin-right: 10px;
font-size: 16px;
";
const SearchBar = () => {
return (
<SearchBarContainer>
<Input type="text" placeholder="Search..." />
<Button>Search</Button>
</SearchBarContainer>
);
};
export default SearchBar;
"""
### 1.2. Separation of Concerns
**Standard:** Practice a clear separation of concerns by dividing components into presentational and container components. Consider also using hooks for more complex logic.
* **Why:** Separating concerns improves code readability, testability, and reusability. Presentational components focus on UI rendering, while container components handle data fetching, state management, and business logic.
* **Do This:**
* Create presentational components that receive data and callbacks via props and focus solely on rendering UI.
* Create container components that manage state, data fetching, and pass necessary data and callbacks to presentational components.
* Use hooks where relevant to extract complicated state logic.
* **Don't Do This:**
* Mix data fetching and business logic within presentational components, making them difficult to test and reuse.
* Write overly complex container components that handle UI rendering logic.
* **Code Example:**
"""jsx
// Presentational Component: ProductCard.jsx
import React from 'react';
import styled from '@emotion/styled';
const Card = styled.div"
border: 1px solid #ccc;
padding: 15px;
margin: 10px;
width: 200px;
text-align: center;
";
const Title = styled.h3"
font-size: 18px;
margin-bottom: 5px;
";
const Price = styled.p"
font-weight: bold;
";
const ProductCard = ({ product }) => {
return (
<Card>
<Title>{product.name}</Title>
<Price>${product.price}</Price>
<img src = {product.image} width = {150} height = {150} alt = {product.name} />
</Card>
);
};
export default ProductCard;
// Container Component: ProductList.jsx
import React, { useState, useEffect } from 'react';
import ProductCard from './ProductCard';
import styled from '@emotion/styled';
const ProductListContainer = styled.div"
display: flex;
flex-wrap: wrap;
justify-content: center;
";
const ProductList = () => {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchProducts = async () => {
try {
const response = await fetch('https://fakestoreapi.com/products');
const data = await response.json();
setProducts(data);
} catch (error) {
console.error("Error fetching products:", error);
}
};
fetchProducts();
}, []);
return (
<ProductListContainer>
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</ProductListContainer>
);
};
export default ProductList;
"""
### 1.3. Using Emotion Themes
**Standard:** Utilize Emotion's theming capabilities to manage and apply consistent styling across your application
* **Why:** Themes offer a centralized way to define and manage styles (colors, fonts, spacing, etc.). This approach ensures consistency, improves maintainability, and simplifies style updates across the application.
* **Do This:**
* Define a theme object containing your style variables.
* Wrap your application with the "<ThemeProvider>" component.
* Access theme values within your styled components using the "theme" prop.
* **Don't Do This:**
* Hardcode style values directly within styled components, making styling updates and consistency difficult to manage.
* Avoid defining a comprehensive theme object. Strive to capture all common styling attributes.
* **Code Example:**
"""jsx
// theme.js
export const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
background: '#f8f9fa',
text: '#212529',
},
fonts: {
main: 'Arial, sans-serif',
secondary: 'Helvetica, sans-serif',
},
spacing: {
small: '8px',
medium: '16px',
large: '24px',
},
};
// App.jsx
import React from 'react';
import { ThemeProvider } from '@emotion/react';
import styled from '@emotion/styled';
import { theme } from './theme';
const AppContainer = styled.div"
background-color: ${props => props.theme.colors.background};
color: ${props => props.theme.colors.text};
font-family: ${props => props.theme.fonts.main};
padding: ${props => props.theme.spacing.medium};
";
const Title = styled.h1"
color: ${props => props.theme.colors.primary};
";
const App = () => {
return (
<ThemeProvider theme={theme}>
<AppContainer>
<Title>My Styled Application</Title>
<p>Using Emotion's theming capabilities.</p>
</AppContainer>
</ThemeProvider>
);
};
export default App;
"""
## 2. Component Implementation
### 2.1. Consistent Styling Approach with "styled"
**Standard:** Use the "styled" API for creating styled components. This is the primary and recomended way to create components with Emotion.
* **Why:** "styled" provides a clean and declarative approach to styling components, offering better readability and maintainability. It leverages tagged template literals for concise CSS-in-JS syntax. It is the primary way one should write Emotion, and avoids less feature-rich approaches.
* **Do This:**
* Use "styled.componentName" to create styled components.
* Leverage template literals to define CSS rules.
* Use props to dynamically style components based on their state or context.
* **Don't Do This:**
* Use inline styles directly within JSX elements, as this reduces maintainability and reusability.
* Overuse global selectors, which can lead to styling conflicts.
* **Code Example:**
"""jsx
import styled from '@emotion/styled';
const PrimaryButton = styled.button"
background-color: ${props => props.primary ? '#007bff' : '#fff'};
color: ${props => props.primary ? '#fff' : '#007bff'};
border: 1px solid #007bff;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: ${props => props.primary ? '#0056b3' : '#e9ecef'};
}
";
const App = () => {
return (
<div>
<PrimaryButton primary>Primary Button</PrimaryButton>
<PrimaryButton>Secondary Button</PrimaryButton>
</div>
);
};
"""
### 2.2. Dynamic Styles with Props
**Standard:** Utilize component props to dynamically style components based on their state or context.
* **Why:** Dynamic styling enables creating versatile components that can adapt to various situations, enhancing reusability and flexibility.
* **Do This:**
* Access props within styled component definitions to apply conditional styles.
* Use ternary operators or logical expressions to handle different styling scenarios.
* **Don't Do This:**
* Overcomplicate prop-based styling with excessive conditional logic.
* Neglect to provide default values for props to avoid unexpected styling issues.
* **Code Example:**
"""jsx
import styled from '@emotion/styled';
const StatusIndicator = styled.div"
width: 20px;
height: 20px;
border-radius: 50%;
background-color: ${props => {
switch (props.status) {
case 'online':
return 'green';
case 'offline':
return 'red';
case 'idle':
return 'yellow';
default:
return 'gray';
}
}};
";
const App = () => {
return (
<div>
<StatusIndicator status="online" />
<StatusIndicator status="offline" />
<StatusIndicator status="idle" />
<StatusIndicator /> {/* Default: Gray */}
</div>
);
};
"""
### 2.3. CSS Prop for Overrides (Use Sparingly)
**Standard:** Use the "css" prop for simple style overrides or one-off styling adjustments, but avoid relying on it for core component styling.
* **Why:** The "css" prop provides a convenient way to apply ad-hoc styles to components without creating new styled components. However, overuse can lead to scattered styles and reduced maintainability.
* **Do This:**
* Use the "css" prop for minor style adjustments that are specific to a particular instance of a component.
* Keep the styles within the "css" prop concise and focused on the specific override.
* **Don't Do This:**
* Use the "css" prop for defining fundamental component styles; instead, use "styled" components.
* Overuse the "css" prop, leading to a lack of consistency and maintainability.
* **Code Example:**
"""jsx
import styled from '@emotion/styled';
import { css } from '@emotion/react';
const Button = styled.button"
background-color: #007bff;
color: white;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
&:hover {
background-color: #0056b3;
}
";
const App = () => {
return (
<div>
<Button>Default Button</Button>
<Button
css={css"
margin-left: 10px;
font-size: 18px;
background-color: #28a745;
&:hover {
background-color: #1e7e34;
}
"}
>
Custom Button
</Button>
</div>
);
};
"""
### 2.4. Composition and Inheritance
**Standard:** Combine Emotion components through composition and inheritance to create more complex and specialized components.
* **Why:** Composition and inheritance promote code reuse and reduce duplication. This also allows you to create highly customized components that can be shared.
* **Do This:**
* Use component composition to combine simpler components into more complex ones.
* Use Emotion's "styled" API to extend existing styled components and override their styles.
* **Don't Do This:**
* Create redundant components with overlapping functionality.
* Overuse inheritance, which can lead to complex and tightly coupled component hierarchies.
* **Code Example:**
"""jsx
import styled from '@emotion/styled';
// Base Button
const BaseButton = styled.button"
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
";
// Primary Button (Inheritance)
const PrimaryButton = styled(BaseButton)"
background-color: #007bff;
color: white;
border: none;
&:hover {
background-color: #0056b3;
}
";
// Outlined Button (Inheritance)
const OutlinedButton = styled(BaseButton)"
background-color: transparent;
color: #007bff;
border: 1px solid #007bff;
&:hover {
background-color: #e9ecef;
}
";
// Button Group (Composition)
const ButtonGroupContainer = styled.div"
display: flex;
";
const ButtonGroup = ({ children }) => {
return <ButtonGroupContainer>{children}</ButtonGroupContainer>;
};
const App = () => {
return (
<div>
<PrimaryButton>Primary</PrimaryButton>
<OutlinedButton>Outlined</OutlinedButton>
<ButtonGroup>
<PrimaryButton>Save</PrimaryButton>
<OutlinedButton>Cancel</OutlinedButton>
</ButtonGroup>
</div>
);
};
"""
### 2.5. Avoiding Global Styles (Generally)
**Standard:** Minimize the use of global styles in components to maintain encapsulation. If you need global styles use the "Global" component provided by Emotion with intention.
* **Why:** Global styles can lead to unintended side effects and styling conflicts across the application. Component-specific styles promote better encapsulation and maintainability.
* **Do This:**
* Use component-specific styles whenever possible.
* If global styles are necessary, use them sparingly and with clear justification.
* **Don't Do This:**
* Overuse global styles, leading to styling conflicts and reduced maintainability.
* Apply global styles indiscriminately.
* **Code Example:**
"""jsx
import styled from '@emotion/styled';
import { Global, css } from '@emotion/react';
const AppContainer = styled.div"
padding: 20px;
";
const Title = styled.h1"
color: #333;
";
const App = () => {
return (
<AppContainer>
<Global
styles={css"
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
}
"}
/>
<Title>My Application</Title>
<p>Component-specific styles.</p>
</AppContainer>
);
};
"""
## 3. Performance Considerations
### 3.1. Minimizing Style Re-calculations
**Standard:** Strive to minimize style re-calculations by reducing unnecessary prop changes and optimizing component rendering.
* **Why:** Excessive style re-calculations can negatively impact performance, especially in complex applications.
* **Do This:**
* Use "React.memo" to prevent unnecessary re-renders of styled components.
* Avoid passing frequently changing props to styled components if they don't affect styling.
* Consider using the "useMemo" hook to memoize complex style calculations.
* **Don't Do This:**
* Pass frequently changing props to styled components without considering their impact on performance.
* Rely on excessively complex style calculations within styled components.
* **Code Example:**
"""jsx
import styled from '@emotion/styled';
import React, { memo } from 'react';
const StyledButton = styled.button"
background-color: ${props => props.primary ? '#007bff' : '#fff'};
color: ${props => props.primary ? '#fff' : '#007bff'};
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
";
const Button = memo(StyledButton);
const App = () => {
const [count, setCount] = React.useState(0);
return (
<div>
<Button primary onClick={() => setCount(count + 1)}>
Increment
</Button>
<p>Count: {count}</p>
</div>
);
};
"""
### 3.2. Server-Side Rendering (SSR)
**Standard:** Implement server-side rendering (SSR) for improved initial page load performance and SEO.
* **Why:** SSR delivers pre-rendered HTML to the client, improving perceived performance and enabling search engine crawlers to index the application content effectively.
* **Do This:**
* Use frameworks like Next.js or Gatsby that provide built-in SSR support for Emotion.
* Follow the framework-specific guidelines for configuring Emotion for SSR.
* **Don't Do This:**
* Neglect to implement SSR for performance-critical applications, missing out on significant rendering optimization.
### 3.3. Code Splitting
**Standard:** Use code splitting to reduce the initial JavaScript bundle size and improve load times using Dynamic imports.
* **Why:** Code splitting reduces the amount of JavaScript the browser needs to download and parse initially. This is done by breaking up the application into smaller chunks that load on demand.
* **Do This:**
* Use dynamic imports ("import()") to load components and modules asynchronously.
* Structure your application to facilitate code splitting by route or feature.
* Ensure that the core of your application loads first.
* **Don't Do This:**
* Create unnecessarily large bundle sizes.
* Implement splitting for components that require immediate availability.
* **Code Example:**
"""jsx
import React, { Suspense } from 'react';
import styled from '@emotion/styled';
const LoadableComponent = React.lazy(() => import('./MyComponent'));
const LoadingFallback = styled.div"
text-align: center;
padding: 20px;
font-style: italic;
";
const App = () => {
return (
<div>
<Suspense fallback={<LoadingFallback>Loading...</LoadingFallback>}>
<LoadableComponent />
</Suspense>
</div>
);
};
export default App;
"""
### 3.4 Vendor Prefixing (Handled by Emotion)
**Standard:** Rely on Emotion's built-in vendor prefixing, and avoid manual prefixing.
* **Why:** Emotion automatically handles vendor prefixing for CSS properties, ensuring compatibility across different browsers. Manual prefixing can lead to duplication and maintenance issues.
* **Do This:**
* Write standard CSS properties without vendor prefixes.
* Trust Emotion to automatically add the necessary prefixes.
* **Don't Do This:**
* Manually add vendor prefixes to CSS properties, leading to unnecessary code and potential inconsistencies.
## 4. Accessibility
### 4.1. Semantic HTML
**Standard:** Use semantic HTML elements as the foundation for styled components, providing inherent accessibility features.
* **Why:** Semantic HTML elements (e.g., "<button>", "<input>", "<nav>", "<article>") provide built-in accessibility features such as proper keyboard navigation, screen reader support, and ARIA roles.
* **Do This:**
* Use appropriate semantic HTML elements as the base for styled components.
* Avoid using generic elements (e.g., "<div>", "<span>") for interactive components where semantic alternatives exist.
* **Don't Do This:**
* Use non-semantic elements for interactive components, reducing accessibility.
* Overuse "<div>" and "<span>" elements without considering semantic alternatives.
* **Code Example:**
"""jsx
import styled from '@emotion/styled';
const StyledButton = styled.button"
background-color: #007bff;
color: white;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
";
const StyledNav = styled.nav"
background-color: #f0f0f0;
padding: 10px;
";
const StyledLink = styled.a"
color: #333;
text-decoration: none;
margin-right: 10px;
";
const App = () => {
return (
<div>
<StyledButton>Click Me</StyledButton>
<StyledNav>
<StyledLink href="#">Home</StyledLink>
<StyledLink href="#">About</StyledLink>
<StyledLink href="#">Contact</StyledLink>
</StyledNav>
</div>
);
};
"""
### 4.2. ARIA Attributes
**Standard:** Use ARIA attributes to enhance the accessibility of complex or custom components that lack native semantic equivalents.
* **Why:** ARIA attributes provide additional information to assistive technologies, such as screen readers, enabling them to understand the purpose and behavior of non-standard UI elements.
* **Do This:**
* Add ARIA attributes (e.g., "aria-label", "aria-describedby", "aria-hidden", "role") to components that require additional accessibility information.
* Ensure that ARIA attributes are used correctly and do not conflict with existing semantic attributes.
* **Don't Do This:**
* Overuse ARIA attributes, as this can lead to unnecessary complexity.
* Use ARIA attributes incorrectly or redundantly.
* Fail to test ARIA attributes with assistive technologies to verify their effectiveness.
* **Code Example:**
"""jsx
import styled from '@emotion/styled';
const StyledTooltip = styled.div"
position: relative;
display: inline-block;
";
const TooltipText = styled.span"
visibility: hidden;
width: 120px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;
opacity: 0;
transition: opacity 0.3s;
${StyledTooltip}:hover & {
visibility: visible;
opacity: 1;
}
";
const App = () => {
return (
<StyledTooltip aria-label="Tooltip Example">
Hover Me
<TooltipText>This is a tooltip</TooltipText>
</StyledTooltip>
);
};
"""
### 4.3. Color Contrast
**Standard:** Ensure sufficient color contrast between text and background colors to meet accessibility guidelines.
* **Why:** Insufficient color contrast can make it difficult for users with visual impairments to read text content.
* **Do This:**
* Use online tools to verify that the color contrast meets WCAG (Web Content Accessibility Guidelines) standards.
* Provide alternative high-contrast themes for users with low vision.
* Avoid using color alone to convey important information.
* **Don't Do This:**
* Use color combinations that provide insufficient contrast.
* Rely solely on color to differentiate elements.
## 5. Testing
### 5.1. Unit Tests
**Standard:** Write unit tests for individual components to ensure they render correctly and behave as expected.
* **Why:** Unit tests provide confidence in component functionality and prevent regressions.
* **Do This:**
* Use testing libraries like Jest and React Testing Library to write unit tests.
* Test component rendering with different props and states.
* Test component behavior in response to user interactions.
* **Don't Do This:**
* Neglect to write unit tests for components, leading to potential bugs and regressions.
* Write incomplete or superficial unit tests that do not thoroughly test component functionality.
### 5.2. Integration Tests
**Standard:** Perform integration tests to ensure components work together correctly within the application context.
* **Why:** Integration tests verify that components interact seamlessly and that data flows correctly between them.
* **Do This:**
* Use tools like Cypress or Selenium to perform integration tests.
* Test component interactions within the application's overall structure.
* Test data flow between components and external services.
* **Don't Do This:**
* Skip integration tests, leading to potential issues arising from component interactions.
* Neglect to test data flow between components and external services.
### 5.3. Visual Regression Tests
**Standard:** Implement visual regression tests to detect unintended visual changes in components.
* **Why:** Visual regression tests help prevent accidental styling changes, ensuring consistent UI across different environments.
* **Do This:**
* Use tools like Storybook and Chromatic to perform visual regression tests.
* Capture snapshots of component renderings and compare them against baseline images.
* Review and approve any visual changes before merging code.
* **Don't Do This:**
* Neglect to perform visual regression tests, leading to potential UI inconsistencies.
* Approve visual changes without careful review, allowing unintended styling changes to slip through.
danielsogl
Created Mar 6, 2025
# Core Architecture Standards for Emotion
This document outlines the core architecture standards for Emotion, a popular CSS-in-JS library. These standards are designed to promote maintainability, performance, and security within Emotion-based projects. This guide focuses on architectural patterns, project structure, organizational principles and coding standards related to these. This document assumes familiarity with React and modern JavaScript.
## 1. Project Structure and Organization
A well-organized project structure significantly contributes to code maintainability and scalability. Choosing an appropriate structure from the outset reduces the likelihood of creating tight coupling and increases the maintainability of individual components in the library.
### 1.1. Standard: Feature-Based Organization
**Do This:** Organize your project by feature. This means grouping related components, styles, and logic within a directory representing a specific feature or module.
**Don't Do This:** Organize your project by technical role (e.g., separating all components, styles, and utilities into separate top-level directories).
**Why:** Feature-based organization enhances modularity, reduces coupling, and makes it easier to locate all files related to a specific functionality. This benefits both initial development and ongoing refactoring efforts. Following this structure ensures developers can rapidly grasp the dependencies of a functionality and limits the possibility of unintended side effects.
**Example:**
"""
src/
├── components/ # Globally available components
├── features/
│ ├── user-profile/ # Feature: User Profile
│ │ ├── components/
│ │ │ ├── ProfileCard.jsx
│ │ │ └── EditProfileForm.jsx
│ │ ├── styles/
│ │ │ ├── ProfileCard.styles.js
│ │ │ └── EditProfileForm.styles.js
│ │ ├── UserProfile.jsx # Main component for this feature
│ │ └── index.js # Exports all components related to user profile
│ ├── dashboard/ # Feature: Dashboard
│ │ ├── components/
│ │ │ ├── SummaryCard.jsx
│ │ │ └── ActivityFeed.jsx
│ │ ├── styles/
│ │ │ ├── SummaryCard.styles.js
│ │ │ └── ActivityFeed.styles.js
│ │ ├── Dashboard.jsx # Main component for this feature
│ │ └── index.js # Exports all dashboard-related components
├── utils/ # Global utility functions
├── App.jsx
└── index.jsx
"""
**Explanation:**
* The "features" directory contains subdirectories, each representing a distinct feature of the application (e.g., "user-profile", "dashboard").
* Each feature directory includes its own "components" and supporting "styles" directories, keeping related code self-contained.
* An "index.js" file within each feature directory serves as the entry point for that feature, exporting the components and any other relevant modules.
* Globally available components reside inside the "components" folder.
* Shared code logic for each feature resides inside the "utils" folder.
### 1.2. Standard: Centralized Theme Configuration
**Do This:** Define a single, centralized theme object that contains all your application's styling variables (colors, fonts, spacing, breakpoints, etc.). Use Emotion's "<ThemeProvider>" to make this theme available to all components.
**Don't Do This:** Define styling variables directly within individual components or sprinkle fixed values throughout your codebase.
**Why:** Centralized theme management promotes consistency, simplifies updates, and allows for easy theming (e.g., light/dark mode). It allows for better code reusability and readability.
**Example:**
"""javascript
// src/theme.js
const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
background: '#f8f9fa',
text: '#212529',
},
fonts: {
main: 'Arial, sans-serif',
heading: 'Georgia, serif',
},
spacing: {
small: '0.5rem',
medium: '1rem',
large: '2rem',
},
breakpoints: {
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
},
};
export default theme;
"""
"""jsx
// src/App.jsx
import React from 'react';
import { ThemeProvider } from '@emotion/react';
import theme from './theme';
import MyComponent from './components/MyComponent';
function App() {
return (
<ThemeProvider theme={theme}>
<MyComponent />
</ThemeProvider>
);
}
export default App;
"""
"""jsx
// src/components/MyComponent.jsx
import React from 'react';
import styled from '@emotion/styled';
const StyledDiv = styled.div"
background-color: ${props => props.theme.colors.background};
color: ${props => props.theme.colors.text};
padding: ${props => props.theme.spacing.medium};
font-family: ${props => props.theme.fonts.main};
";
function MyComponent() {
return (
<StyledDiv>
Hello, Emotion!
</StyledDiv>
);
}
export default MyComponent;
"""
**Explanation:**
* "theme.js" defines the application's theme, containing colors, fonts etc.
* "<ThemeProvider>" makes the theme available to all child components.
* "styled" components access theme values via "props.theme".
### 1.3. Standard: Component Composition and Abstraction
**Do This:** Favor component composition and create reusable, abstract components that encapsulate styling and behavior.
**Don't Do This:** Create monolithic components with tightly coupled styling and logic, or repeat styling code across multiple components.
**Why:** Component composition simplifies code reuse, reduces complexity, and improves overall component maintainability. Abstract components are easier to test and update without affecting dependent components.
**Example:**
"""jsx
// src/components/Button.jsx
import React from 'react';
import styled from '@emotion/styled';
const Button = styled.button"
background-color: ${props => props.theme.colors.primary};
color: white;
padding: ${props => props.theme.spacing.medium};
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
opacity: 0.8;
}
";
function MyButton({ children, onClick }) {
return (
<Button onClick={onClick}>
{children}
</Button>
);
}
export default MyButton;
"""
"""jsx
// src/features/user-profile/components/EditProfileForm.jsx
import React from 'react';
import Button from '../../../components/Button';
function EditProfileForm() {
return (
<form>
{/* Form fields */}
<Button type="submit">Save Changes</Button>
</form>
);
}
export default EditProfileForm;
"""
**Explanation:**
* The "Button" component encapsulates the basic button styling.
* The "EditProfileForm" component reuses the "Button" component, avoiding duplication of styling code.
## 2. Emotion-Specific Architectural Patterns
Emotion provides unique and highly flexible tools for managing CSS in your React applications. The following structural elements are worth noting.
### 2.1. Standard: Using "styled" for Component-Level Styling
**Do This:** Employ Emotion's "styled" factory to create styled components. This approach allows you to define styles directly alongside your components, leveraging JavaScript's capabilities for dynamic styling.
**Don't Do This:** Write raw CSS strings or rely on external stylesheets for component-specific styling, thereby losing colocation benefits.
**Why:** "styled" components enable a tight coupling between React components and their styles, improving code readability and maintainability. It supports dynamic styling based on component props or theme variables.
**Example:**
"""jsx
import React from 'react';
import styled from '@emotion/styled';
const Title = styled.h1"
font-size: 2em;
color: ${props => props.theme.colors.primary};
";
const Subtitle = styled.h2({
fontSize: '1.5em',
color: 'gray',
});
function MyComponent() {
return (
<div>
<Title>Welcome</Title>
<Subtitle>to my page</Subtitle>
</div>
);
}
export default MyComponent;
"""
**Explanation:**
* The "Title" and "Subtitle" components are styled using Emotion's "styled" factory, defining CSS rules directly within the component definition.
* The "Title" component accesses theme variables through "props.theme".
* The styles for the "Subtitle" component are written as object.
* The styles are co-located with the javascript code, leading to more consistent and maintainable code.
### 2.2. Standard: The "css" Prop for Dynamic and Contextual Styles
**Do This:** Use the "css" prop in your React components for more flexible and contextual styling. It's especially valuable for defining styles that depend on component state or external factors.
**Don't Do This:** Overuse the "css" prop for fundamental styling, which is better handled by "styled" components.
**Why:** The "css" prop gives you fine-grained control over styling and can be used to modify style based on Javascript rules.
**Example:**
"""jsx
import React, { useState } from 'react';
import styled from '@emotion/styled';
import { css } from '@emotion/react';
const Button = styled.button"
background-color: ${props => props.theme.colors.primary};
color: white;
padding: ${props => props.theme.spacing.medium};
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
opacity: 0.8;
}
";
function ExampleComponent() {
const [isActive, setIsActive] = useState(false);
return (
<Button
css={css"
${isActive && "
background-color: ${props => props.theme.colors.secondary};
&:hover {
opacity: 1;
}
"}
"}
onClick={() => setIsActive(!isActive)}
>
{isActive ? 'Active' : 'Inactive'}
</Button>
);
}
export default ExampleComponent;
"""
**Explanation:**
* The "ExampleComponent" uses a state variable "isActive" to conditionally apply styles using the "css" prop.
* When the button is active, its background color changes to a secondary color and the hover opacity is set to 1.
* For complex conditional styling of Emotion components, the "css" prop gives you more freedom when writing stylistic rules within the component itself.
### 2.3. Standard: Global Styles with "Global"
**Do This:** Use the "<Global>" component to define styles that should apply to the entire application, such as resets, base typography, or theme-specific overrides.
**Don't Do This:** Define global styles within individual components, polluting component scope and creating conflicts.
**Why:** The "<Global>" component ensures that global styles are applied consistently across the application, promoting uniformity and avoiding style conflicts.
**Example:**
"""jsx
// src/App.jsx
import React from 'react';
import { ThemeProvider, Global } from '@emotion/react';
import theme from './theme';
import MyComponent from './components/MyComponent';
function App() {
return (
<ThemeProvider theme={theme}>
<Global
styles={"
body {
font-family: ${theme.fonts.main};
background-color: ${theme.colors.background};
color: ${theme.colors.text};
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6 {
font-family: ${theme.fonts.heading};
}
"}
/>
<MyComponent />
</ThemeProvider>
);
}
export default App;
"""
**Explanation:**
* The "<Global>" component is used within the "<ThemeProvider>" to define global styles for the "body" and heading elements.
* These styles apply consistently across the entire application, ensuring a uniform visual appearance.
* Note that the Global component can access the "theme" variable as well.
## 3. Advanced Patterns and Best Practices
These best practices lead to better performance and maintainability of Emotion-based code.
### 3.1. Standard: Server-Side Rendering (SSR) with Emotion
**Do This:** Configure Emotion for server-side rendering to improve initial page load performance and SEO. Use Emotion's "CacheProvider" to deduplicate styles and inject them into the HTML.
**Don't Do This:** Neglect SSR for Emotion applications, leading to poor user experience and SEO results.
**Why:** SSR improves perceived performance and allows search engines to crawl and index your content more effectively
**Example:**
"""javascript
// pages/_document.jsx (Next.js example)
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { extractCritical } from '@emotion/server';
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
const page = await ctx.renderPage();
const styles = extractCritical(page.html);
return {
...initialProps,
...page,
styles: (
<>
{initialProps.styles}
<style
data-emotion-css={styles.ids.join(' ')}
dangerouslySetInnerHTML={{ __html: styles.css }}
/>
</>
),
};
}
render() {
return (
<Html>
<Head>
<meta name="emotion-insertion-point" content="" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
"""
**Explanation:**
* The "getInitialProps" method extracts critical Emotion styles during server-side rendering.
* These styles are then injected into the HTML document.
* The "emotion-insertion-point" meta tag ensures that Emotion styles are inserted correctly.
### 3.2. Standard: Optimizing Performance
**Do This:**
* Minimize re-renders by using "React.memo" on styled components, especially if they receive props that might trigger unnecessary updates.
* Leverage CSS variables (custom properties) in your theme and styled components to reduce the amount of generated CSS.
* Use "useTheme" hook to optimize theme access in functional components.
**Don't Do This:**
* Ignore performance considerations when styling complex components or handling dynamic styles.
* Use inline styles excessively, as they can negatively impact performance.
**Why:**
Performance optimization improves the user experience. React rendering of DOM updates can be a bottleneck in dynamic applications.
**Example:**
"""jsx
import React from 'react';
import styled from '@emotion/styled';
import { useTheme } from '@emotion/react';
const OptimizedComponent = styled.div"
background-color: ${props => props.theme.colors.primary};
color: white;
padding: ${props => props.theme.spacing.medium};
";
const MyComponent = React.memo(function({data}) {
const theme = useTheme();
return (
<OptimizedComponent theme={theme}>
{data}
</OptimizedComponent>
);
});
export default MyComponent;
"""
**Explanation:**
* "React.memo" prevents unnecessary re-renders of the "MyComponent" if its props haven't changed.
* "useTheme" hook improves theme variable access.
### 3.3. Standard: Testing Emotion Components
**Do This:** Implement unit and integration tests for your Emotion components to ensure they render correctly, apply the right styles, and respond appropriately to user interactions. Use testing libraries like Jest and React Testing Library.
**Don't Do This:** Omit testing for Emotion components, leading to regressions and unpredictable styling behavior.
**Why**. Thorough testing ensures that components behave as expected and are resilient to changes.
**Example:**
"""jsx
// src/components/Button.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import Button from './Button';
import { ThemeProvider } from '@emotion/react';
import theme from '../../theme';
test('renders button with correct text', () => {
render(
<ThemeProvider theme={theme}>
<Button>Click Me</Button>
</ThemeProvider>
);
const buttonElement = screen.getByText('Click Me');
expect(buttonElement).toBeInTheDocument();
});
test('styles are applied correctly', () => {
render(
<ThemeProvider theme={theme}>
<Button>Click Me</Button>
</ThemeProvider>
);
const buttonElement = screen.getByText('Click Me');
expect(buttonElement).toHaveStyle("background-color: ${theme.colors.primary}");
});
"""
**Explanation:**
* The test suite uses React Testing Library to render the "Button" component within a "<ThemeProvider>".
* It verifies that the button renders with the correct text and that the styles are applied according to the theme.
### 3.4. Standard: Handling Complex Styles
**Do This:** For complex component styles, consider creating separate style definition files to improve readability and maintainability. Use javascript objects or Emotion's tagged template literals for more complex CSS rules.
**Don't Do This:** Embed lengthy or intricate styles directly within your components, making them harder to understand and modify.
**Why:** Separating style definitions from components enhances code organization and reduces complexity. Using template literals allows for more dynamic and intuitive style definitions.
**Example:**
"""jsx
// src/features/user-profile/styles/ProfileCard.styles.js
import { css } from '@emotion/react';
export const cardStyle = css"
background-color: white;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
";
export const titleStyle = css"
font-size: 1.5em;
margin-bottom: 8px;
";
"""
"""jsx
// src/features/user-profile/components/ProfileCard.jsx
import React from 'react';
import styled from '@emotion/styled';
import { cardStyle, titleStyle } from '../styles/ProfileCard.styles';
const StyledCard = styled.div"
${cardStyle}
";
const StyledTitle = styled.h2"
${titleStyle}
";
function ProfileCard({ title, children }) {
return (
<StyledCard>
<StyledTitle>{title}</StyledTitle>
{children}
</StyledCard>
);
}
export default ProfileCard;
"""
**Explanation:**
* The style definitions for the "ProfileCard" and its title are extracted into a separate file ("ProfileCard.styles.js").
* The javascript expressions are combined using template literals to express the styles for the styled components.
* This approach improves the readability and maintainability of the "ProfileCard" component by keeping its code concise and focused on its core functionality.
By adhering to these core architecture standards for Emotion, developers can build scalable, maintainable, and high-performing applications with consistent styling and a well-defined structure. This document provides a solid foundation for establishing best practices and guidelines within your Emotion-based projects.
danielsogl
Created Mar 6, 2025
# State Management Standards for Emotion
This document outlines the coding standards for managing application state within Emotion-styled React components. It aims to provide clear guidelines for data flow, reactivity, and integration with state management libraries, emphasizing maintainability, performance, and best practices aligned with the latest Emotion features and React ecosystem.
## 1. Choosing a State Management Approach
The choice of state management approach significantly impacts the structure, maintainability, and performance of your Emotion-styled application. There is no one-size-fits-all solution; the best approach depends on the complexity, scale, and team familiarity.
### 1.1. React Context API
**Use Case:** Simple state management for theming, user context, or localized settings that affect rendering across multiple components without prop drilling. Ideal for application-wide theming integration with Emotion.
**Standards:**
* **Do This:** Use "React.createContext" to define the context and a provider to wrap the application or relevant subtrees.
* **Don't Do This:** Overuse Context for frequently changing state that could introduce unnecessary re-renders. Avoid storing large, complex objects directly in context unless you memoize the provider's value.
**Why:** Context API provides a built-in mechanism for dependency injection, simplifying access to global state and facilitating testing.
**Code Example:**
"""jsx
// ThemeContext.js
import React, { createContext, useState, useContext } from 'react';
import { ThemeProvider } from '@emotion/react';
const ThemeContext = createContext({
theme: 'light',
toggleTheme: () => {},
});
export const ThemeProviderWrapper = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};
const themeValue = {
theme,
toggleTheme,
};
return (
<ThemeContext.Provider value={themeValue}>
<ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>
{children}
</ThemeProvider>
</ThemeContext.Provider>
);
};
export const useTheme = () => useContext(ThemeContext);
// lightTheme and darkTheme are defined elsewhere with Emotion theme objects
const lightTheme = {
colors: {
primary: '#007bff',
background: '#fff',
text: '#000',
},
};
const darkTheme = {
colors: {
primary: '#66b3ff',
background: '#333',
text: '#fff',
},
};
// Component using the theme
import React from 'react';
import styled from '@emotion/styled';
import { useTheme } from './ThemeContext';
const Button = styled.button"
background-color: ${props => props.theme.colors.primary};
color: ${props => props.theme.colors.text};
padding: 10px;
border: none;
cursor: pointer;
";
const ExampleComponent = () => {
const { theme, toggleTheme } = useTheme();
return (
<div>
<p>Current theme: {theme}</p>
<Button onClick={toggleTheme}>Toggle Theme</Button>
</div>
);
};
export default ExampleComponent;
"""
**Anti-Pattern:** Deeply nested context providers without strategies to prevent unnecessary renders when context values are updated.
### 1.2. Redux/Zustand/Recoil
**Use Case:** Complex applications that manage a large amount of global state, require centralized logic, and benefit from predictable state updates and time-travel debugging. Suitable for managing application data fetched from APIs, user authentication information, or complex forms.
**Standards:**
* **Do This:** Use Redux Toolkit for Redux, Zustand's simplicity for smaller projects, or Recoil for fine-grained state subscriptions with React's concurrent mode. Utilize selectors to derive data in selectors to prevent unnecessary component re-renders.
* **Don't Do This:** Reach directly into the store from Emotion-styled components. Connect component props directly to the store without memoization and selectors. Overuse global store if component-level state is sufficient.
**Why:** Redux/Zustand/Recoil provide a robust and predictable state management solution, especially when dealing with asynchronous actions and complex data dependencies.
**Code Example:**
"""jsx
// Redux example with @reduxjs/toolkit
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, selectCount } from './counterSlice'; // Assuming a counter slice
import styled from '@emotion/styled';
const CounterDisplay = styled.div"
font-size: 20px;
margin-bottom: 10px;
";
const CounterButton = styled.button"
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 5px;
";
const Counter = () => {
const count = useSelector(selectCount);
const dispatch = useDispatch();
return (
<div>
<CounterDisplay>Count: {count}</CounterDisplay>
<CounterButton onClick={() => dispatch(increment())}>Increment</CounterButton>
<CounterButton onClick={() => dispatch(decrement())}>Decrement</CounterButton>
</div>
);
};
export default Counter;
// counterSlice.js (Redux Toolkit slice)
import { createSlice } from '@reduxjs/toolkit';
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
},
});
export const { increment, decrement } = counterSlice.actions;
export const selectCount = (state) => state.counter.value; // Selector
export default counterSlice.reducer;
"""
**Anti-Pattern:** Mutating state directly within Redux reducers or Zustand setters. Deeply nested selectors that are difficult to maintain.
### 1.3. Jotai/Zustand for Simpler Global State
**Use Case:** Lightweight global state management with minimal boilerplate, suitable for sharing state across components without the complexity of Redux.
**Standards:**
* **Do This:** Define atoms (Jotai) or stores (Zustand) with clear roles and responsibilities. Use derived atoms in Jotai for computed values based on other atoms. Utilize selector functions for specific data extractions from Zustand stores.
* **Don't Do This:** Create excessively large atoms/stores. Directly mutate atom values/store state outside of the defined update functions.
**Why:** Offer a simpler API and reduced bundle size compared to Redux while still providing global state management capabilities optimized for performance. The Reactivity reduces re-renders.
**Code Example:**
"""jsx
// Jotai example
import React from 'react';
import { useAtom } from 'jotai';
import { atom } from 'jotai';
import styled from '@emotion/styled';
const CounterDisplay = styled.div"
font-size: 20px;
margin-bottom: 10px;
";
const CounterButton = styled.button"
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 5px;
";
const countAtom = atom(0);
const Counter = () => {
const [count, setCount] = useAtom(countAtom);
return (
<div>
<CounterDisplay>Count: {count}</CounterDisplay>
<CounterButton onClick={() => setCount(count + 1)}>Increment</CounterButton>
<CounterButton onClick={() => setCount(count - 1)}>Decrement</CounterButton>
</div>
);
};
export default Counter;
"""
**Anti-Pattern:** Using overly complex or nested patterns within Jotai atom definitions.
### 1.4. Component-Level State (useState/useReducer)
**Use Case:** Managing state that is local to a specific component or a small, isolated part of the application. Considered the first choice for simple state management.
**Standards:**
* **Do This:** Prefer "useState" for simpler state management. Use "useReducer" for complex state logic involving multiple sub-values or complex updates.
* **Don't Do This:** Lift state unnecessarily into parent components. Use Component-level state for data that needs to be shared across multiple unrelated components.
**Why:** Keeps the complexity of state management contained within the component itself, preventing global namespace pollution.
**Code Example:**
"""jsx
import React, { useState } from 'react';
import styled from '@emotion/styled';
const ToggleButton = styled.button"
background-color: ${props => props.isOn ? '#4CAF50' : '#f44336'};
color: white;
padding: 10px 20px;
border: none;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 5px;
";
const ToggleComponent = () => {
const [isOn, setIsOn] = useState(false);
return (
<div>
<p>Toggle is: {isOn ? 'ON' : 'OFF'}</p>
<ToggleButton isOn={isOn} onClick={() => setIsOn(!isOn)}>
{isOn ? 'Turn OFF' : 'Turn ON'}
</ToggleButton>
</div>
);
};
export default ToggleComponent;
"""
**Anti-Pattern:** Duplicating state logic across multiple components instead of extracting into a custom hook.
## 2. Integrating State with Emotion
Efficiently connecting chosen state management and Emotion styles is crucial for creating maintainable and performant components.
### 2.1. Using State to Dynamically Apply Styles
**Standards:**
* **Do This:** Use props derived from state (either component-level or global) directly within Emotion's styled components or "css" prop. Use template literals for cleaner syntax when injecting state values into styles.
* **Don't Do This:** Dynamically construct class names based on state. Directly manipulate the DOM or styles outside of Emotion's styling system based on state changes.
**Why:** Emotion automatically handles CSS injection and updates, reducing manual DOM manipulations and improving performance.
**Code Example:**
"""jsx
import React from 'react';
import styled from '@emotion/styled';
const Button = styled.button"
background-color: ${props => props.isActive ? 'green' : 'red'};
color: white;
padding: 10px 15px;
border: none;
cursor: pointer;
";
const EnhancedButton = ({ isActive, children }) => {
return <Button isActive={isActive}>{children}</Button>;
};
export default EnhancedButton;
"""
**Anti-Pattern:* Applying styles via "style" prop when equivalent functionality can be achieved using "styled components" or "css" prop with state variable interpolation.
### 2.2. Theme Provider Integration
**Standards:**
* **Do This:** Wrap your application with Emotion's "<ThemeProvider>" and manage the theme as part of your application state. Use a state management library that integrates well with React Context, such as Zustand (for simple theme management) or Redux (for complex scenarios).
* **Don't Do This:** Hardcode theme values directly into your Emotion-styled components. Place "<ThemeProvider>" inside individual/leaf components since Emotion themes should be application-wide.
**Why:** Enables consistent theming across the application and simplifies theme switching based on user preferences or other contextual factors.
**Code Example:**
"""jsx
// App.jsx
import React, { useState } from 'react';
import styled, { ThemeProvider } from '@emotion/react';
// Define Themes
const lightTheme = {
colors: {
background: '#fff',
text: '#000',
},
};
const darkTheme = {
colors: {
background: '#000',
text: '#fff',
},
};
const Container = styled.div"
background-color: ${props => props.theme.colors.background};
color: ${props => props.theme.colors.text};
padding: 20px;
";
function App() {
const [theme, setTheme] = useState(lightTheme);
const toggleTheme = () => {
setTheme(theme === lightTheme ? darkTheme : lightTheme);
};
return (
<ThemeProvider theme={theme}>
<Container>
<h1>Themed App</h1>
<button onClick={toggleTheme}>Toggle Theme</button>
</Container>
</ThemeProvider>
);
}
export default App;
"""
**Anti-Pattern:** Using multiple "<ThemeProvider>" instances with conflicting themes.
## 3. Performance Considerations
Managing state efficiently in combination with Emotion styles is essential for minimizing re-renders and ensuring optimal application performance.
### 3.1. Memoization Techniques
**Standards:**
* **Do This:** Utilize "React.memo" to prevent re-renders of Emotion-styled components when their props haven't changed. Create memoized selectors using libraries like Reselect for Redux or derived atoms in Jotai/Zustand when using derived data in Emotion styles.
* **Don't Do This:** Overuse "React.memo" without profiling potential performance gains vs. the cost of equality checks.
**Why:** Memoization avoids unnecessary style re-applications and reduces the overall rendering workload, especially in large applications.
**Code Example:**
"""jsx
import React, { memo } from 'react';
import styled from '@emotion/styled';
const ButtonStyled = styled.button"
background-color: ${props => props.color};
color: white;
padding: 10px 15px;
border: none;
cursor: pointer;
";
const Button = memo(ButtonStyled, (prevProps, nextProps) => {
// Custom comparison function. Only re-render if 'color' prop changes.
return prevProps.color === nextProps.color;
});
export default Button;
"""
**Anti-Pattern:** Creating inline functions as props to memoized components, as this will always trigger a re-render due to referential inequality.
### 3.2. Avoiding Unnecessary Re-Renders
**Standards:**
* **Do This:** Ensure props passed to Emotion-styled components are stable and don't change unnecessarily. Batch state updates using "useState" functional updates or "useReducer" to minimize re-renders.
* **Don't Do This:** Update state frequently with the same values. Trigger state updates in "render" methods because this leads to infinite loops.
**Why:** Reducing re-renders improves application responsiveness and prevents performance bottlenecks, particularly in complex UI interactions and data visualizations.
**Code Example:**
"""jsx
import React, { useState, useCallback } from 'react';
import styled from '@emotion/styled';
const Input = styled.input"
padding: 8px;
border: 1px solid #ccc;
";
const Label = styled.label"
margin-right: 10px;
";
const MyComponent = () => {
const [inputValue, setInputValue] = useState('');
const handleInputChange = useCallback((event) => {
setInputValue(event.target.value);
}, []);
return (
<div>
<Label>Enter text:</Label>
<Input type="text" value={inputValue} onChange={handleInputChange} />
<p>You entered: {inputValue}</p>
</div>
);
};
export default MyComponent;
"""
**Anti-Pattern:** Passing entire state objects as props to Emotion-styled components when only specific properties are needed.
## 4. Security Considerations
### 4.1. Sanitizing User Inputs
**Standards:**
* **Do This:** Sanitize and validate user inputs before using them to dynamically generate styles. Utilize libraries like DOMPurify to prevent Cross-Site Scripting (XSS) attacks.
* **Don't Do This:** Directly inject user-provided data into CSS without proper escaping or sanitization.
**Why:** Prevents malicious code from manipulating styles and compromising the application.
**Code Example:**
"""jsx
import React from 'react';
import styled from '@emotion/styled';
import DOMPurify from 'dompurify';
const DynamicStyle = styled.div"
/* Example of UNSAFE styling, DO NOT DO THIS DIRECTLY! */
/* color: ${props => props.unsafeColor}; */
";
const SafeStyle = styled.div"
color: ${props => props.safeColor};
";
const ComponentWithDynamicStyle = ({ userInput }) => {
const safeColor = DOMPurify.sanitize(userInput);
return (
<div>
{/* UNSAFE */}
{/* <DynamicStyle unsafeColor={userInput}>This is dynamically styled text</DynamicStyle> */}
{/* SAFE */}
<SafeStyle safeColor={safeColor}>This is dynamically styled text</SafeStyle>
</div>
);
};
export default ComponentWithDynamicStyle;
"""
**Anti-Pattern:** Trusting the user to provide safe CSS values without any form of validation or sanitization.
### 4.2. Preventing CSS Injection Attacks
**Standards:**
* **Do This:** Implement Content Security Policy (CSP) headers to restrict the sources from which the browser can load resources, mitigating the risk of CSS injection attacks.
* **Don't Do This:** Allow external stylesheets or inline styles from untrusted sources.
**Why:** Restricts the impact of potentially malicious CSS code by limiting its access to application resources, reducing the attack surface significantly.
## 5. Testing Strategies
### 5.1. Unit Testing Emotion Components with State
**Standards:**
* **Do This:** Use testing libraries like Jest and React Testing Library to render and interact with Emotion-styled components. Mock state management hooks and providers to isolate components during testing. Assert that styles are applied correctly based on state changes.
* **Don't Do This:** Rely solely on snapshot testing without verifying component behavior based on state. Create brittle tests that tightly couple to implementation details.
**Why:** Ensures that Emotion-styled components render correctly based on different state scenarios and that style updates are triggered as expected.
"""jsx
// Example Jest test with React Testing Library
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter'; // Component defined in previous examples
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
test('increments the counter on button click', () => {
render(
<Provider store={store}>
<Counter />
</Provider>
);
const incrementButton = screen.getByText('Increment');
fireEvent.click(incrementButton);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
"""
Consistent adherence to these standards will promote code maintainability, improve performance, and reduce the risk of potential security vulnerabilities within Emotion-based React applications.
danielsogl
Created Mar 6, 2025
# Testing Methodologies Standards for Emotion
This document outlines the testing methodologies standards for developing applications using Emotion. It provides guidelines encompassing unit, integration, and end-to-end testing, tailored specifically for Emotion-based projects. Following these standards ensures maintainable, performant, and reliable code.
## 1. Unit Testing
### 1.1. Importance of Unit Tests
Unit tests are crucial for verifying individual components and functions in isolation. They ensure that each part of the system works as expected. Within the context of Emotion, this often means testing components that rely on Emotion for styling.
* **Do This:** Write unit tests for all React components that use Emotion for styling, validating rendered styles, and component behavior.
* **Don't Do This:** Neglect testing Emotion components under the assumption that CSS-in-JS styling is "just CSS."
### 1.2. Recommended Testing Frameworks and Libraries
* **Jest:** A popular JavaScript testing framework.
* **React Testing Library:** Provides utility functions that are focused on testing component behavior rather than implementation details.
* **@emotion/test-utils:** Utilities from Emotion that support testing styled components, themes, and keyframes.
### 1.3. Unit Testing Guidelines
* **Test Focused Components:** Each unit test should focus on a single component or a small, related set of components.
* **Mock Dependencies:** Use mocking to isolate the component being tested.
* **Clear Assertions:** Use descriptive assertions to clearly define the expected behavior.
* **Test All Scenarios:** Test positive, negative, and edge cases to ensure robustness.
### 1.4. Code Examples
#### 1.4.1. Testing a Styled Component
"""jsx
// src/components/Button.jsx
import styled from '@emotion/styled';
const Button = styled.button"
background-color: ${props => props.primary ? 'palevioletred' : 'white'};
color: ${props => props.primary ? 'white' : 'palevioletred'};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
";
export default Button;
"""
"""jsx
// src/components/Button.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import Button from './Button';
describe('Button Component', () => {
it('renders with default styles', () => {
render(<Button>Click me</Button>);
const buttonElement = screen.getByText('Click me');
expect(buttonElement).toHaveStyle('background-color: white');
expect(buttonElement).toHaveStyle('color: palevioletred');
});
it('renders with primary styles', () => {
render(<Button primary>Click me</Button>);
const buttonElement = screen.getByText('Click me');
expect(buttonElement).toHaveStyle('background-color: palevioletred');
expect(buttonElement).toHaveStyle('color: white');
});
});
"""
* **Explanation:** This example demonstrates how to test a styled component, by checking background and color properties. "screen.getByText" is used to find the element, and "toHaveStyle" checks the css.
#### 1.4.2. Testing a Component with Theme Provider
"""jsx
// src/components/ThemedButton.jsx
import React from 'react';
import styled from '@emotion/styled';
import { useTheme } from '@emotion/react';
const ThemedButton = styled.button"
background-color: ${props => props.theme.colors.primary};
color: ${props => props.theme.colors.secondary};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid ${props => props.theme.colors.primary};
border-radius: 3px;
";
export default ThemedButton;
"""
"""jsx
// src/components/ThemedButton.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from '@emotion/react';
import ThemedButton from './ThemedButton';
const theme = {
colors: {
primary: 'darkblue',
secondary: 'white',
},
};
describe('ThemedButton Component', () => {
it('renders with theme styles', () => {
render(
<ThemeProvider theme={theme}>
<ThemedButton>Click me</ThemedButton>
</ThemeProvider>
);
const buttonElement = screen.getByText('Click me');
expect(buttonElement).toHaveStyle('background-color: darkblue');
expect(buttonElement).toHaveStyle('color: white');
});
});
"""
* **Explanation:** When testing components that consume a theme provided by Emotion's "<ThemeProvider>", you need to wrap the component within "<ThemeProvider>" in your test setup. This ensures the component receives the expected theme context.
#### 1.4.3. Testing with "as" Prop
"""jsx
// src/components/LinkButton.jsx
import styled from '@emotion/styled';
import { Link } from 'react-router-dom';
const LinkButton = styled.button"
/* Styles for the button */
background-color: lightblue;
color: black;
padding: 10px 20px;
border: none;
cursor: pointer;
/* Common styles */
&:hover {
background-color: darkblue;
color: white;
}
"
const StyledLinkButton = styled(Link)"
/* Inherits styles from Button */
${LinkButton}
text-decoration: none;
";
export default StyledLinkButton;
"""
"""jsx
// src/components/LinkButton.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom'; // Import BrowserRouter
import StyledLinkButton from './LinkButton';
describe('StyledLinkButton Component', () => {
it('renders as a link with correct styles', () => {
render(
<BrowserRouter> {/* Wrap with BrowserRouter */}
<StyledLinkButton to="/test">Click me</StyledLinkButton>
</BrowserRouter>
);
const linkElement = screen.getByRole('link', { name: 'Click me' });
expect(linkElement).toHaveStyle('background-color: lightblue');
expect(linkElement).toHaveAttribute('href', '/test');
});
});
"""
* **Explanation:** A common pattern is using the "as" prop which allows you to render a styled component as a different HTML element or React component. If rendering a "<Link>" component from "react-router-dom" it is essential to wrap the component under test in a "<BrowserRouter>".
### 1.5. Common Anti-Patterns
* **Testing Implementation Details:** Tests should focus on the component's behavior, not its internal implementation.
* **Lack of Assertions:** Every test must include assertions to confirm the expected outcome.
* **Ignoring Edge Cases:** Failing to test edge cases can lead to unexpected bugs in production.
## 2. Integration Testing
### 2.1. Purpose of Integration Tests
Integration tests verify the interaction between different parts of the system. They ensure that components work together correctly. In Emotion-based projects, this means testing the interaction between styled components, themes, and other React components.
* **Do This:** Write integration tests for components that interact with other components, the Emotion theme, or external APIs.
* **Don't Do This:** Skip integration tests for complex components that rely on multiple dependencies.
### 2.2. Strategies for Integration Testing
* **Top-Down:** Start with high-level components and gradually integrate lower-level components.
* **Bottom-Up:** Start with low-level components and gradually integrate higher-level components.
* **Sandwich:** A combination of top-down and bottom-up approaches.
### 2.3. Integration Testing Guidelines
* **Minimal Mocks:** Use mocks sparingly to focus on the integration between actual components.
* **Real Data:** Use realistic data to simulate real-world scenarios.
* **Test Key Interactions:** Focus on testing the most critical interactions between components.
### 2.4. Code Examples
#### 2.4.1. Testing Component Integration with Theme and Styled Components
"""jsx
// src/components/ComplexComponent.jsx
import React from 'react';
import styled from '@emotion/styled';
import { useTheme } from '@emotion/react';
const StyledContainer = styled.div"
background-color: ${props => props.theme.colors.background};
padding: 20px;
";
const Title = styled.h2"
color: ${props => props.theme.colors.primary};
";
function ComplexComponent({ title, children }) {
const theme = useTheme();
return (
<StyledContainer>
<Title>{title}</Title>
<div>{children}</div>
</StyledContainer>
);
}
export default ComplexComponent;
"""
"""jsx
// src/components/ComplexComponent.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from '@emotion/react';
import ComplexComponent from './ComplexComponent';
const theme = {
colors: {
primary: 'darkblue',
secondary: 'white',
background: 'lightgray',
},
};
describe('ComplexComponent Integration', () => {
it('renders with theme styles and child content', () => {
render(
<ThemeProvider theme={theme}>
<ComplexComponent title="My Title">
<p>Some content</p>
</ComplexComponent>
</ThemeProvider>
);
const container = screen.getByRole('div');
expect(container).toHaveStyle('background-color: lightgray');
const titleElement = screen.getByText('My Title');
expect(titleElement).toHaveStyle('color: darkblue');
const contentElement = screen.getByText('Some content');
expect(contentElement).toBeInTheDocument();
});
});
"""
* **Explanation:** This test checks how "ComplexComponent" renders with different styles. It makes sure a child is being rendered properly as well.
### 2.5. Common Anti-Patterns
* **Over-Mocking:** Mocks should be used sparingly. Test the real interactions between components where possible.
* **Ignoring Boundary Conditions:** Test how components behave at the boundaries of their expected input ranges.
* **Complex Setups:** Keep test setups as simple as possible to reduce the risk of test failures due to unrelated issues.
## 3. End-to-End (E2E) Testing
### 3.1. Purpose of E2E Tests
End-to-end tests simulate real user scenarios, verifying that the entire application works correctly from the user's perspective. They test the integration of all layers, including the UI, backend, and database.
* **Do This:** Write E2E tests for critical user flows, such as login, form submission, and data retrieval.
* **Don't Do This:** Rely solely on unit and integration tests; E2E tests catch issues related to the entire system working together.
### 3.2. Recommended Frameworks and Libraries
* **Cypress:** A popular E2E testing framework.
* **Playwright:** Another E2E testing framework gaining popularity.
### 3.3. E2E Testing Guidelines
* **Real User Scenarios:** Tests should closely mimic real user behavior.
* **Stable Test Data:** Use consistent test data to avoid flaky tests.
* **Clear Assertions:** Use descriptive assertions to validate the expected outcome.
* **Isolate Environments:** Run E2E tests in a dedicated testing environment.
### 3.4. Code Examples
#### 3.4.1. Cypress E2E Test
"""javascript
// cypress/e2e/spec.cy.js
describe('My First Test', () => {
it('Visits the app root url', () => {
cy.visit('/');
cy.contains('h1', 'Welcome');
});
it('Verify button navigates to another page', () => {
cy.visit('/');
cy.get('button').contains('Go to About').click();
cy.url().should('include', '/about');
cy.contains('h1', 'About Us');
});
});
"""
* **Explanation:** This example visits the application and verifies the existance of a "<h1/>" tag with the text of "Welcome." The second test example tests clicking a button, and checking that the url subsequently contains "/about" and that the correct page title is displayed.
### 3.5. Common Anti-Patterns
* **Flaky Tests:** Unstable tests that pass and fail intermittently.
* **Over-Reliance on UI Selectors:** Prefer using data-* attributes for selectors to make tests more robust.
* **Slow Tests:** Optimize tests for speed by minimizing setup and teardown overhead.
* **Ignoring Accessibility:** Tests should also verify that the application is accessible.
## 4. Testing Emotion-Specific Features
### 4.1. Keyframes
When testing components that use keyframes, it is important to verify that the animation is correctly applied and behaves as expected.
"""jsx
// src/components/AnimatedComponent.jsx
import React from 'react';
import styled, { keyframes } from '@emotion/styled';
const rotate = keyframes"
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
";
const AnimatedDiv = styled.div"
display: inline-block;
animation: ${rotate} 2s linear infinite;
padding: 2rem 1rem;
font-size: 1.2rem;
";
export default AnimatedDiv;
"""
"""jsx
// src/components/AnimatedComponent.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import AnimatedDiv from './AnimatedComponent';
describe('AnimatedDiv Component', () => {
it('applies the rotate animation', () => {
render(<AnimatedDiv>Rotating Content</AnimatedDiv>);
const animatedDiv = screen.getByText('Rotating Content');
expect(animatedDiv).toHaveStyleRule('animation', expect.stringContaining('rotate'));
});
});
"""
* **Explanation:** Keyframes can be tested by assertion using "toHaveStyleRule" and testing if the final html element has the rotation animation applied.
### 4.2. Global Styles
Test if global styles are correctly applied to the document.
"""jsx
// src/components/GlobalStyles.jsx
import React from 'react';
import { Global, css } from '@emotion/react';
const GlobalStyles = () => (
<Global
styles={css"
body {
font-family: 'Arial, sans-serif';
background-color: #f0f0f0;
}
"}
/>
);
export default GlobalStyles;
"""
"""jsx
// src/components/GlobalStyles.test.jsx
import React from 'react';
import { render, screen, cleanup } from '@testing-library/react';
import GlobalStyles from './GlobalStyles';
describe('GlobalStyles Component', () => {
afterEach(cleanup);
it('applies global styles', () => {
render(<GlobalStyles />);
expect(document.body).toHaveStyle('font-family: Arial, sans-serif');
expect(document.body).toHaveStyle('background-color: #f0f0f0');
});
});
"""
* **Explanation:** Similar to keyframes you can verify properties using "toHaveStyle" after rendering. Note the use of "cleanup" to prevent style bleeding between tests.
### 4.3. Testing with "css" prop
Emotion’s "css" prop allows you to pass CSS directly to React components.
"""jsx
// src/components/CssPropComponent.jsx
import React from 'react';
function CssPropComponent({ children }) {
return (
<div
css={{
backgroundColor: 'lightblue',
padding: '10px',
borderRadius: '5px',
}}
>
{children}
</div>
);
}
export default CssPropComponent;
"""
"""jsx
// src/components/CssPropComponent.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import CssPropComponent from './CssPropComponent';
describe('CssPropComponent', () => {
it('renders with styles from css prop', () => {
render(<CssPropComponent>Hello</CssPropComponent>);
const divElement = screen.getByText('Hello').closest('div');
expect(divElement).toHaveStyle('background-color: lightblue');
expect(divElement).toHaveStyle('padding: 10px');
});
});
"""
* **Explanation:** Test components using the "css" prop in a similar way to testing styled components, using "toHaveStyle". "closest('div')" is used to check the style of the parent div.
## 5. Advanced Testing Techniques
### 5.1. Visual Regression Testing
Visual regression testing involves capturing screenshots of components and comparing them to baseline images to detect unintended visual changes. This approach is especially useful for ensuring that styling changes do not introduce visual regressions.
* **Tools:**
* **Storybook:** Good for creating and managing UI tests
* **Chromatic:** Cloud-based visual testing and UI review tool for Storybook.
* **Percy:** Visual review platform.
### 5.2. Performance Testing
Performance testing measures the rendering performance of Emotion components. This helps identify and address performance bottlenecks.
* **Techniques:**
* **Profiling:** Use browser developer tools to profile component rendering.
* **Benchmarking:** Use libraries like "benchmark.js" to measure the performance of specific code snippets.
* **Lighthouse:** Evaluate the performance of your components with Lighthouse.
### 5.3. Accessibility Testing
Accessibility testing ensures that Emotion components are accessible to users with disabilities.
* **Tools:**
* **React Axe:** Adds accessibility testing directly into your React components.
* **WAVE:** Web Accessibility Evaluation Tool.
## 6. Continuous Integration (CI)
* **Do This:** Integrate testing into your CI/CD pipeline.
* **Don't Do This:** Manually run tests only before deployment.
### 6.1. Integrating Testing into CI/CD
* **Automated Testing:** Run unit, integration, and E2E tests automatically on every commit and pull request.
* **Reporting:** Generate test reports and display them in your CI/CD pipeline.
* **Failure Handling:** Fail the build if any tests fail.
## 7. Conclusion
Adhering to these testing methodologies standards will help ensure the quality, reliability, and maintainability of Emotion-based applications. By incorporating unit, integration, and end-to-end testing, teams can catch potential issues early in the development process, reduce bugs, and improve the overall user experience. Remember to stay updated with the latest Emotion features and adapt these standards accordingly to leverage new testing capabilities and best practices.
danielsogl
Created Mar 6, 2025
# API Integration Standards for Emotion
This document outlines the coding standards for integrating Emotion with external APIs and backend services in React applications. These standards aim to ensure maintainable, performant, secure, and scalable code while leveraging best practices offered by Emotion.
## 1. Architectural Considerations for API Integration with Emotion
### 1.1. Separation of Concerns
**Do This:**
* Separate API calling logic from presentational components.
* Use custom hooks or dedicated service modules for handling API requests.
* Style components based on props passed from the connected logic, not directly within the API calling logic itself.
**Don't Do This:**
* Make API calls directly within Emotion styled components.
* Mix API fetching logic with styling logic.
* Update the UI directly inside the "fetch" or "axios" calls within the style definition.
**Why:** This separation makes components more reusable, testable, and easier to maintain. Components become purely presentational, driven by data, which improves code clarity and reduces side effects.
**Example (Anti-Pattern):**
"""jsx
// Avoid this pattern! Mixing API call and styling.
import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
const StyledDiv = styled.div"
background-color: ${() => {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(json => setData(json));
}, []);
if (data) {
return 'lightgreen';
} else {
return 'lightcoral';
}
}};
padding: 20px;
border: 1px solid black;
";
function MyComponent() {
return <StyledDiv>Data Area</StyledDiv>;
}
export default MyComponent;
"""
**Example (Correct Pattern):**
"""jsx
// Correct pattern: Separating API logic and styling.
import styled from '@emotion/styled';
import { useData } from './useData'; // Custom hook
const StyledDiv = styled.div"
background-color: ${props => (props.hasData ? 'lightgreen' : 'lightcoral')};
padding: 20px;
border: 1px solid black;
";
function MyComponent() {
const { data, loading, error } = useData('https://api.example.com/data');
return (
<StyledDiv hasData={!!data}>
{loading ? 'Loading...' : error ? 'Error!' : 'Data Area'}
</StyledDiv>
);
}
export default MyComponent;
// useData.js (Custom Hook)
import { useState, useEffect } from 'react';
export function useData(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP error! Status: ${response.status}");
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (e) {
setError(e);
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
"""
### 1.2. State Management
**Do This:**
* Use React Context, Redux, Zustand, or similar state management libraries to store and share fetched data across components.
* Consider using React Query or SWR for data fetching, caching, and state synchronization.
**Don't Do This:**
* Pass API data as props through multiple layers of unrelated components (prop drilling).
* Rely solely on component-level state for data that needs to be accessed and updated in multiple places.
**Why:** Efficient state management reduces unnecessary re-renders and simplifies data flow, leading to better performance and maintainability.
**Example (Using React Context):**
"""jsx
// DataContext.js
import React, { createContext, useState, useEffect, useContext } from 'react';
const DataContext = createContext();
export const DataProvider = ({ children }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
const json = await response.json();
setData(json);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
const value = { data, loading, error };
return <DataContext.Provider value={value}>{children}</DataContext.Provider>;
};
export const useData = () => useContext(DataContext);
// MyComponent.js
import React from 'react';
import styled from '@emotion/styled';
import { useData } from './DataContext';
const StyledDiv = styled.div"
background-color: ${props => (props.hasData ? 'lightgreen' : 'lightcoral')};
padding: 20px;
border: 1px solid black;
";
function MyComponent() {
const { data, loading, error } = useData();
return (
<StyledDiv hasData={!!data}>
{loading ? 'Loading...' : error ? 'Error!' : 'Data Area'}
</StyledDiv>
);
}
export default MyComponent;
"""
### 1.3 Error Handling
**Do This:**
* Implement robust error handling to gracefully manage API request failures.
* Display user-friendly error messages within Emotion-styled components using conditional rendering.
* Log errors to a central logging service for monitoring and debugging.
**Don't Do This:**
* Ignore potential errors from API requests.
* Display raw error messages directly to the user.
**Why:** Proper error handling ensures a stable and user-friendly application experience, even when API request fail. Detailed logging enables rapid identification and troubleshooting of issues.
**Example:**
"""jsx
import styled from '@emotion/styled';
import { useData } from './useData'; // Custom hook
const StyledDiv = styled.div"
background-color: ${props => (props.hasData ? 'lightgreen' : 'lightcoral')};
padding: 20px;
border: 1px solid black;
color: ${props => props.isError ? 'red' : 'black'};
";
const ErrorMessage = styled.p"
color: red;
font-style: italic;
";
function MyComponent() {
const { data, loading, error } = useData('https://api.example.com/data');
return (
<StyledDiv hasData={!!data} isError={!!error}>
{loading ? 'Loading...' : error ? <ErrorMessage>Error: {error.message}</ErrorMessage> : 'Data Area'}
</StyledDiv>
);
}
export default MyComponent;
// useData.js
import { useState, useEffect } from 'react';
export function useData(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP error! Status: ${response.status}");
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (e) {
setError(e);
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
"""
## 2. Best Practices for Connecting to Backend Services
### 2.1. Asynchronous Operations
**Do This:**
* Use "async/await" syntax for handling asynchronous API calls to improve code readability.
* Handle promises correctly using "try...catch" blocks to catch errors.
* Display status updates like "Loading..." while waiting for data.
**Don't Do This:**
* Use ".then()" and ".catch()" chains excessively, as they can make the code harder to read.
* Block the UI thread while waiting for API responses.
**Why:** "async/await" makes asynchronous code look and behave a bit more like synchronous code, which improves readability and maintainability.
**Example:**
"""jsx
import styled from '@emotion/styled';
import { useState, useEffect } from 'react';
const LoadingIndicator = styled.p"
font-style: italic;
color: grey;
";
const StyledDiv = styled.div"
padding: 20px;
border: 1px solid black;
margin-bottom: 10px;
";
const ErrorMessage = styled.p"
color: red;
font-style: italic;
";
function AsyncComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error("HTTP error! status: ${response.status}");
}
const jsonData = await response.json();
setData(jsonData);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) {
return <LoadingIndicator>Loading data...</LoadingIndicator>;
}
if (error) {
return <ErrorMessage>Error: {error.message}</ErrorMessage>;
}
return (
<StyledDiv>
{data && <p>Data: {JSON.stringify(data)}</p>}
</StyledDiv>
);
}
export default AsyncComponent;
"""
### 2.2. Data Transformation
**Do This:**
* Transform API responses into a format suitable for your styled components.
* Centralize transformation logic in helper functions or service modules.
**Don't Do This:**
* Perform data transformation directly within Emotion styled components.
* Pass raw API data directly to styled components without any processing.
**Why:** Transformation clarifies and simplifies the data used within styled components, enhancing readability and testability. Reduces the complexity of the styled component definitions.
**Example:**
"""jsx
// Transform data to format suitable for styling needs
// utils/transformData.js
export const transformData = (apiData) => {
if (!apiData) return null;
return {
formattedName: apiData.firstName + ' ' + apiData.lastName,
isActive: apiData.status === 'active',
profilePicture: apiData.profileImageURL || 'default_image.png'
};
};
// MyComponent.js
import styled from '@emotion/styled';
import { useState, useEffect } from 'react';
import { transformData } from './utils/transformData';
const ProfileCard = styled.div"
background-color: ${props => (props.isActive ? 'lightgreen' : 'lightcoral')};
padding: 15px;
border: 1px solid black;
border-radius: 8px;
";
const ProfileImage = styled.img"
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
";
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/user');
const apiData = await response.json();
const transformedData = transformData(apiData);
setData(transformedData);
};
fetchData();
}, []);
if (!data) return <p>Loading...</p>;
return (
<ProfileCard isActive={data.isActive}>
<ProfileImage src={data.profilePicture} alt="Profile" />
<p>Name: {data.formattedName}</p>
</ProfileCard>
);
}
export default MyComponent;
"""
### 2.3. Caching Strategies
**Do This:**
* Implement caching strategies (e.g., using "localStorage", "sessionStorage", or dedicated caching libraries like "react-query", "swr") to reduce unnecessary API calls.
* Use optimistic updates to provide a responsive user experience.
**Don't Do This:**
* Cache sensitive data without proper encryption and security measures.
* Over-cache data that changes frequently.
**Why:** Caching improves application performance by reducing latency and network traffic.
**Example (Basic LocalStorage Caching):**
"""jsx
import styled from '@emotion/styled';
import { useState, useEffect } from 'react';
const StyledDiv = styled.div"
padding: 20px;
border: 1px solid black;
";
function CachingComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const cachedData = localStorage.getItem('apiData');
if (cachedData) {
setData(JSON.parse(cachedData));
return; // Use cached data if available
}
const response = await fetch('https://api.example.com/data');
const jsonData = await response.json();
setData(jsonData);
localStorage.setItem('apiData', JSON.stringify(jsonData)); // Cache data
};
fetchData();
}, []);
return (
<StyledDiv>
{data ? <p>Data: {JSON.stringify(data)}</p> : <p>Loading...</p>}
</StyledDiv>
);
}
export default CachingComponent;
"""
## 3. Integrating Form Submissions With Emotion
### 3.1. Controlled Components
**Do This:**
* Use controlled components for form inputs to manage input state and validation effectively.
* Apply Emotion styles directly on form and form input elements for consistent styling and theming.
**Don't Do This:**
* Rely on uncontrolled components with direct DOM manipulation for handling form submissions as it reduces maintainability.
* Avoid mixing form submission logic directly with style definitions and instead use styles for presentation and custom handlers for form logic.
**Why:** Controlled components provide better control over form data, enabling advanced validation and manipulation. Styled-components help you encapsulate styles within reusable and composable components.
**Example:**
"""jsx
import styled from '@emotion/styled';
import { useState } from 'react';
const Form = styled.form"
display: flex;
flex-direction: column;
width: 300px;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
";
const Input = styled.input"
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 4px;
";
const Button = styled.button"
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: #0056b3;
}
";
const Label = styled.label"
margin-bottom: 5px;
font-weight: bold;
";
function MyFormComponent() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
const handleSubmit = async (event) => {
event.preventDefault();
try {
const response = await fetch('https://api.example.com/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, email, message }),
});
if (response.ok) {
// Handle success
alert('Form submitted successfully!');
} else {
// Handle error
alert('Form submission failed.');
}
} catch (error) {
console.error('Error submitting form:', error);
alert('An error occurred while submitting the form.');
}
};
return (
<Form onSubmit={handleSubmit}>
<Label htmlFor="name">Name:</Label>
<Input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<Label htmlFor="email">Email:</Label>
<Input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<Label htmlFor="message">Message:</Label>
<Input
as="textarea" // Use 'as' prop for textarea
id="message"
value={message}
onChange={(e) => setMessage(e.target.value)}
rows="4"
required
/>
<Button type="submit">Submit</Button>
</Form>
);
}
export default MyFormComponent;
"""
### 3.2 Validation
**Do This:**
* Implement client-side and server-side validation to ensure data integrity.
* Use appropriate validation libraries like Formik or Yup for complex forms.
**Don't Do This:**
* Rely solely on client-side validation. Always validate data on the server as well.
* Skip validation for performance reasons.
**Why:** Validation reduces errors and security vulnerabilities by ensuring that the data meets the application's requirements.
## 4. Security Considerations
### 4.1. Authentication and Authorization
**Do This:**
* Implement secure authentication and authorization mechanisms to protect API endpoints.
* Store tokens securely using HTTP-only cookies or alternative secure storage methods.
* Utilize environment variables to store API keys and sensitive credentials.
**Don't Do This:**
* Store API keys or sensitive data directly in client-side code or version control.
* Expose sensitive information in API responses.
**Why:** Secure authentication and authorization are essential to protect your application and user data from unauthorized access.
### 4.2. Cross-Origin Resource Sharing (CORS)
**Do This:**
* Configure CORS on the server-side to allow requests only from authorized domains.
* Understand CORS implications when making cross-origin API requests.
**Don't Do This:**
* Disable CORS completely as it can introduce security risks.
* Use wildcard origins ("*") in production CORS configurations.
**Why:** Proper CORS configuration prevents malicious websites from accessing your API.
### 4.3. Input Sanitization
**Do This:**
* Sanitize user inputs to prevent cross-site scripting (XSS) and other injection attacks.
* Use appropriate encoding techniques when displaying data in Emotion styled components.
**Don't Do This:**
* Directly render unsanitized user inputs in styled components.
* Trust user-supplied data without proper validation and sanitization.
**Why:** Input sanitization is critical for preventing security vulnerabilities and protecting your application from malicious attacks.
## 5. Performance Optimization
### 5.1. Code Splitting
**Do This:**
* Implement code splitting using React.lazy and Suspense to load components and code related to API integration only when needed.
**Why:** Reduces the initial load time and improves overall application performance.
### 5.2. Memoization
**Do This:**
* Use "React.memo" to memoize Emotion styled components that depend on props retrieved from APIs, preventing unnecessary re-renders.
**Why:** Memoization can significantly improve rendering performance.
**Example:**
"""jsx
import React from 'react';
import styled from '@emotion/styled';
const StyledDiv = styled.div"
padding: 20px;
border: 1px solid black;
background-color: ${props => (props.isActive ? 'lightgreen' : 'white')};
";
const MemoizedComponent = React.memo(function MyComponent({ data }) {
console.log('Component rendered'); // Check rendering
return (
<StyledDiv isActive={data.isActive}>
Name: {data.name}
</StyledDiv>
);
}, (prevProps, nextProps) => {
// Custom comparison to prevent re-renders if data hasn't changed significantly
return prevProps.data.name === nextProps.data.name && prevProps.data.isActive === nextProps.data.isActive;
});
export default MemoizedComponent;
"""
### 5.3. Debouncing and Throttling :
**Do This:**
* Apply debouncing or throttling techniques to limit the rates at which API calls are initiated in response to user actions like typing in a search bar or scrolling, reducing server load and improving UI responsiveness.
## 6. Versioning of Endpoints
**Do This:**
* Incorporate versioning into your API endpoints (e.g., "/api/v1/resource") to maintain compatibility as your API evolves, allowing clients to upgrade at their own pace.
* Clearly document any breaking changes introduced in new versions of the API.
**Don't Do This:**
* Make changes to an API without considering the impact on existing consumers.
* Forcing immediate updates for all clients when new features are released.
## Conclusion
These standards provide a guide to building robust, maintainable, and secure applications that integrate Emotion with external APIs. By adhering to these guidelines, developers can ensure high-quality code and a consistent development experience.
danielsogl
Created Mar 6, 2025