MoreRSS

site iconThe Practical DeveloperModify

A constructive and inclusive social network for software developers.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of The Practical Developer

Getting Started with React Offcanvas: Building Side Panels

2026-01-20 00:46:06

React Offcanvas is a simple library for creating offcanvas (side panel) components in React applications. It provides an easy way to display content in a slide-out panel that overlays or pushes the main content, perfect for navigation menus, filters, and additional information panels. This guide walks through setting up and creating offcanvas panels using React Offcanvas with React, from installation to a working implementation. This is part 43 of a series on using React Offcanvas with React.

Prerequisites

Before you begin, make sure you have:

  • Node.js version 14.0 or higher installed
  • npm, yarn, or pnpm package manager
  • A React project (version 16.8 or higher) or create-react-app setup
  • Basic knowledge of React hooks (useState)
  • Familiarity with JavaScript/TypeScript

Installation

Install React Offcanvas using your preferred package manager:

npm install react-offcanvas

Or with yarn:

yarn add react-offcanvas

Or with pnpm:

pnpm add react-offcanvas

After installation, your package.json should include:

{
  "dependencies": {
    "react-offcanvas": "^0.4.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}

Project Setup

React Offcanvas requires minimal setup. Import the component and styles:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

First Example / Basic Usage

Let's create a simple offcanvas panel. Create a new file src/OffcanvasExample.jsx:

// src/OffcanvasExample.jsx
import React, { useState } from 'react';
import Offcanvas from 'react-offcanvas';

function OffcanvasExample() {
  const [isOpen, setIsOpen] = useState(false);

  const toggleOffcanvas = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div style={{ padding: '20px' }}>
      <h2>Offcanvas Example</h2>
      <button
        onClick={toggleOffcanvas}
        style={{
          padding: '10px 20px',
          backgroundColor: '#007bff',
          color: 'white',
          border: 'none',
          borderRadius: '4px',
          cursor: 'pointer'
        }}
      >
        Open Offcanvas
      </button>
      <Offcanvas
        width={300}
        transitionDuration={300}
        isMenuOpen={isOpen}
        effect="overlay"
        position="right"
      >
        <div style={{ padding: '20px' }}>
          <h3>Offcanvas Panel</h3>
          <p>This is the offcanvas content.</p>
          <button
            onClick={toggleOffcanvas}
            style={{
              padding: '8px 16px',
              backgroundColor: '#dc3545',
              color: 'white',
              border: 'none',
              borderRadius: '4px',
              cursor: 'pointer',
              marginTop: '20px'
            }}
          >
            Close
          </button>
        </div>
      </Offcanvas>
    </div>
  );
}

export default OffcanvasExample;

Update your App.jsx:

// src/App.jsx
import React from 'react';
import OffcanvasExample from './OffcanvasExample';
import './App.css';

function App() {
  return (
    <div className="App">
      <OffcanvasExample />
    </div>
  );
}

export default App;

This creates a basic offcanvas panel that slides in from the right when the button is clicked.

Understanding the Basics

React Offcanvas provides several key features:

  • Offcanvas component: The main component that renders the side panel
  • isMenuOpen prop: Controls whether the offcanvas is open or closed
  • width prop: Sets the width of the offcanvas panel
  • position prop: Controls which side the panel slides in from (left, right, top, bottom)
  • effect prop: Controls how the panel appears (overlay, push, etc.)
  • transitionDuration: Controls animation speed

Key concepts:

  • State Management: Use useState to track whether the offcanvas is open or closed
  • Toggle Function: Create a function to toggle the offcanvas state
  • Positioning: Choose which side the panel slides in from
  • Effects: Different effects like overlay, push, or reveal
  • Styling: Customize the appearance with inline styles or CSS classes

Here's an example with different positions and effects:

// src/MultipleOffcanvasExample.jsx
import React, { useState } from 'react';
import Offcanvas from 'react-offcanvas';

function MultipleOffcanvasExample() {
  const [leftOpen, setLeftOpen] = useState(false);
  const [rightOpen, setRightOpen] = useState(false);
  const [topOpen, setTopOpen] = useState(false);

  return (
    <div style={{ padding: '20px' }}>
      <h2>Multiple Offcanvas Examples</h2>
      <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap', marginTop: '20px' }}>
        <button
          onClick={() => setLeftOpen(!leftOpen)}
          style={{
            padding: '10px 20px',
            backgroundColor: '#28a745',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Open Left
        </button>
        <button
          onClick={() => setRightOpen(!rightOpen)}
          style={{
            padding: '10px 20px',
            backgroundColor: '#007bff',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Open Right
        </button>
        <button
          onClick={() => setTopOpen(!topOpen)}
          style={{
            padding: '10px 20px',
            backgroundColor: '#ffc107',
            color: 'black',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Open Top
        </button>
      </div>

      <Offcanvas
        width={250}
        isMenuOpen={leftOpen}
        effect="overlay"
        position="left"
      >
        <div style={{ padding: '20px' }}>
          <h3>Left Panel</h3>
          <p>This panel slides in from the left.</p>
          <button onClick={() => setLeftOpen(false)}>Close</button>
        </div>
      </Offcanvas>

      <Offcanvas
        width={300}
        isMenuOpen={rightOpen}
        effect="overlay"
        position="right"
      >
        <div style={{ padding: '20px' }}>
          <h3>Right Panel</h3>
          <p>This panel slides in from the right.</p>
          <button onClick={() => setRightOpen(false)}>Close</button>
        </div>
      </Offcanvas>

      <Offcanvas
        width="100%"
        isMenuOpen={topOpen}
        effect="overlay"
        position="top"
      >
        <div style={{ padding: '20px' }}>
          <h3>Top Panel</h3>
          <p>This panel slides in from the top.</p>
          <button onClick={() => setTopOpen(false)}>Close</button>
        </div>
      </Offcanvas>
    </div>
  );
}

export default MultipleOffcanvasExample;

Practical Example / Building Something Real

Let's build a navigation menu with an offcanvas panel:

// src/NavigationWithOffcanvas.jsx
import React, { useState } from 'react';
import Offcanvas from 'react-offcanvas';

function NavigationWithOffcanvas() {
  const [isOpen, setIsOpen] = useState(false);

  const menuItems = [
    { id: 1, label: 'Home', href: '#home' },
    { id: 2, label: 'About', href: '#about' },
    { id: 3, label: 'Services', href: '#services' },
    { id: 4, label: 'Portfolio', href: '#portfolio' },
    { id: 5, label: 'Contact', href: '#contact' }
  ];

  const toggleMenu = () => {
    setIsOpen(!isOpen);
  };

  return (
    <div>
      <nav style={{
        backgroundColor: '#2c3e50',
        padding: '15px 30px',
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center',
        boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
      }}>
        <div style={{ color: 'white', fontSize: '24px', fontWeight: 'bold' }}>
          MyApp
        </div>
        <button
          onClick={toggleMenu}
          style={{
            padding: '8px 16px',
            backgroundColor: 'transparent',
            border: '2px solid white',
            color: 'white',
            borderRadius: '4px',
            cursor: 'pointer',
            fontSize: '16px'
          }}
        >
          ☰ Menu
        </button>
      </nav>

      <Offcanvas
        width={280}
        isMenuOpen={isOpen}
        effect="overlay"
        position="right"
        transitionDuration={300}
      >
        <div style={{
          padding: '30px 20px',
          height: '100%',
          backgroundColor: '#34495e',
          color: 'white'
        }}>
          <div style={{
            display: 'flex',
            justifyContent: 'space-between',
            alignItems: 'center',
            marginBottom: '30px',
            paddingBottom: '20px',
            borderBottom: '2px solid rgba(255, 255, 255, 0.3)'
          }}>
            <h2 style={{ margin: 0 }}>Navigation</h2>
            <button
              onClick={toggleMenu}
              style={{
                background: 'none',
                border: 'none',
                color: 'white',
                fontSize: '24px',
                cursor: 'pointer',
                padding: '5px 10px'
              }}
            >
              ×
            </button>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
            {menuItems.map(item => (
              <a
                key={item.id}
                href={item.href}
                onClick={toggleMenu}
                style={{
                  color: 'white',
                  textDecoration: 'none',
                  padding: '15px',
                  borderRadius: '8px',
                  transition: 'background-color 0.3s',
                  fontSize: '18px'
                }}
                onMouseEnter={(e) => e.target.style.backgroundColor = 'rgba(255, 255, 255, 0.1)'}
                onMouseLeave={(e) => e.target.style.backgroundColor = 'transparent'}
              >
                {item.label}
              </a>
            ))}
          </div>
        </div>
      </Offcanvas>

      <div style={{ padding: '40px' }}>
        <h1>Welcome to My App</h1>
        <p>Click the menu button to open the offcanvas navigation panel.</p>
      </div>
    </div>
  );
}

export default NavigationWithOffcanvas;

Now create a filter panel component:

// src/FilterPanel.jsx
import React, { useState } from 'react';
import Offcanvas from 'react-offcanvas';

function FilterPanel() {
  const [isOpen, setIsOpen] = useState(false);
  const [filters, setFilters] = useState({
    category: '',
    priceRange: '',
    sortBy: ''
  });

  const handleFilterChange = (name, value) => {
    setFilters(prev => ({ ...prev, [name]: value }));
  };

  const applyFilters = () => {
    console.log('Applied filters:', filters);
    setIsOpen(false);
  };

  const clearFilters = () => {
    setFilters({ category: '', priceRange: '', sortBy: '' });
  };

  return (
    <div style={{ padding: '20px' }}>
      <button
        onClick={() => setIsOpen(true)}
        style={{
          padding: '10px 20px',
          backgroundColor: '#007bff',
          color: 'white',
          border: 'none',
          borderRadius: '4px',
          cursor: 'pointer'
        }}
      >
        Open Filters
      </button>

      <Offcanvas
        width={320}
        isMenuOpen={isOpen}
        effect="overlay"
        position="right"
      >
        <div style={{ padding: '30px 20px', height: '100%' }}>
          <div style={{
            display: 'flex',
            justifyContent: 'space-between',
            alignItems: 'center',
            marginBottom: '30px',
            paddingBottom: '20px',
            borderBottom: '2px solid #ddd'
          }}>
            <h2 style={{ margin: 0 }}>Filters</h2>
            <button
              onClick={() => setIsOpen(false)}
              style={{
                background: 'none',
                border: 'none',
                fontSize: '24px',
                cursor: 'pointer',
                padding: '5px 10px'
              }}
            >
              ×
            </button>
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
            <div>
              <label style={{ display: 'block', marginBottom: '8px', fontWeight: '500' }}>
                Category
              </label>
              <select
                value={filters.category}
                onChange={(e) => handleFilterChange('category', e.target.value)}
                style={{
                  width: '100%',
                  padding: '8px',
                  border: '1px solid #ddd',
                  borderRadius: '4px'
                }}
              >
                <option value="">All Categories</option>
                <option value="electronics">Electronics</option>
                <option value="clothing">Clothing</option>
                <option value="books">Books</option>
                <option value="home">Home & Garden</option>
              </select>
            </div>

            <div>
              <label style={{ display: 'block', marginBottom: '8px', fontWeight: '500' }}>
                Price Range
              </label>
              <select
                value={filters.priceRange}
                onChange={(e) => handleFilterChange('priceRange', e.target.value)}
                style={{
                  width: '100%',
                  padding: '8px',
                  border: '1px solid #ddd',
                  borderRadius: '4px'
                }}
              >
                <option value="">All Prices</option>
                <option value="0-50">$0 - $50</option>
                <option value="50-100">$50 - $100</option>
                <option value="100-200">$100 - $200</option>
                <option value="200+">$200+</option>
              </select>
            </div>

            <div>
              <label style={{ display: 'block', marginBottom: '8px', fontWeight: '500' }}>
                Sort By
              </label>
              <select
                value={filters.sortBy}
                onChange={(e) => handleFilterChange('sortBy', e.target.value)}
                style={{
                  width: '100%',
                  padding: '8px',
                  border: '1px solid #ddd',
                  borderRadius: '4px'
                }}
              >
                <option value="">Default</option>
                <option value="price-asc">Price: Low to High</option>
                <option value="price-desc">Price: High to Low</option>
                <option value="name-asc">Name: A to Z</option>
                <option value="name-desc">Name: Z to A</option>
              </select>
            </div>
          </div>

          <div style={{
            display: 'flex',
            gap: '10px',
            marginTop: '30px',
            paddingTop: '20px',
            borderTop: '2px solid #ddd'
          }}>
            <button
              onClick={applyFilters}
              style={{
                flex: 1,
                padding: '12px',
                backgroundColor: '#007bff',
                color: 'white',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer',
                fontSize: '16px'
              }}
            >
              Apply Filters
            </button>
            <button
              onClick={clearFilters}
              style={{
                flex: 1,
                padding: '12px',
                backgroundColor: '#6c757d',
                color: 'white',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer',
                fontSize: '16px'
              }}
            >
              Clear
            </button>
          </div>
        </div>
      </Offcanvas>
    </div>
  );
}

export default FilterPanel;

Update your App.jsx:

// src/App.jsx
import React from 'react';
import NavigationWithOffcanvas from './NavigationWithOffcanvas';
import FilterPanel from './FilterPanel';
import './App.css';

function App() {
  return (
    <div className="App">
      <NavigationWithOffcanvas />
      <div style={{ padding: '40px' }}>
        <FilterPanel />
      </div>
    </div>
  );
}

export default App;

This example demonstrates:

  • Navigation menu with offcanvas panel
  • Filter panel with form controls
  • Multiple offcanvas panels
  • Different positions (left, right, top)
  • State management for open/close
  • Custom styling and theming

Common Issues / Troubleshooting

  1. Offcanvas not appearing: Make sure the isMenuOpen prop is set to true and the component is properly rendered. Check that you're managing the state correctly with useState.

  2. Panel not closing: Ensure you have a close button or function that sets isMenuOpen to false. The offcanvas won't close automatically unless you handle the state.

  3. Styling issues: If the offcanvas doesn't look right, check for CSS conflicts. You can customize the appearance using inline styles on the content div inside the Offcanvas component.

  4. Position not working: Make sure you're using a valid position value: 'left', 'right', 'top', or 'bottom'. The default is usually 'left'.

  5. Animation not smooth: Adjust the transitionDuration prop to control animation speed. Higher values (in milliseconds) create slower animations.

  6. Content overflow: If content is too long, add scrolling to the offcanvas content div using overflow-y: auto in the style.

Next Steps

Now that you have a basic understanding of React Offcanvas:

  • Learn about different effects (overlay, push, reveal)
  • Explore advanced positioning options
  • Implement keyboard navigation for accessibility
  • Add animations and transitions
  • Integrate with React Router for navigation
  • Learn about other offcanvas libraries (react-sidebar, react-slide-menu)
  • Check the official repository: https://github.com/vutran/react-offcanvas
  • Look for part 44 of this series for more advanced topics

Summary

You've successfully set up React Offcanvas in your React application and created side panels for navigation and filters. React Offcanvas provides a simple solution for displaying content in slide-out panels with smooth animations.

SEO Keywords

react-offcanvas
React side panel
react-offcanvas tutorial
React slide-out menu
react-offcanvas installation
React offcanvas component
react-offcanvas example
React side navigation
react-offcanvas setup
React panel component
react-offcanvas customization
React overlay panel
react-offcanvas positioning
React side menu
react-offcanvas getting started

Stop Fighting with NestJS Modules: Meet Rikta

2026-01-20 00:45:07

You love NestJS. The decorators feel elegant. The structure keeps your codebase clean. TypeScript integration works well.

Then you add a new service.

You import it in one module. Export it in another. Inject it somewhere else. Suddenly, you get this error:

Nest can't resolve dependencies of the UserService (?). 
Please make sure that the argument at index [0] 
is available in the current context.

Sound familiar?

The Module Problem

NestJS modules solve a real problem: dependency boundaries. They prevent chaos in large codebases.

But they come at a cost.

Every new service requires you to:

  1. Create the service with @Injectable()
  2. Add it to a module's providers array
  3. Add it to the exports array if other modules need it
  4. Import the module in every place that uses it

Here's what a typical NestJS module looks like:

// user.module.ts
@Module({
  imports: [
    DatabaseModule,
    ConfigModule,
    AuthModule,
    LoggingModule,
  ],
  controllers: [UserController],
  providers: [
    UserService,
    UserRepository,
    UserValidator,
    UserMapper,
  ],
  exports: [UserService, UserRepository],
})
export class UserModule {}

This is boilerplate. Pure configuration overhead.

And it grows. Fast.

A medium-sized app has 20+ modules. Each module has 5-10 providers. You spend more time managing arrays than writing business logic.

The worst part? A typo in any array breaks your entire dependency graph. Debugging takes hours.

What If You Didn't Need Modules?

Rikta is a new TypeScript framework built on Fastify. It keeps the parts developers love about NestJS (decorators, DI, structure) and removes the module configuration entirely.

No imports arrays.
No exports arrays.
No providers arrays.

Decorate your class. Everything works.

// user.service.ts
import { Injectable } from '@riktajs/core';

@Injectable()
export class UserService {
  getUsers() {
    return ['Alice', 'Bob'];
  }
}
// user.controller.ts
import { Controller, Get, Autowired } from '@riktajs/core';
import { UserService } from './user.service';

@Controller('/users')
export class UserController {
  @Autowired()
  private userService!: UserService;

  @Get()
  getUsers() {
    return this.userService.getUsers();
  }
}

That's it. No module file. No registration. Rikta scans your code at startup and resolves dependencies automatically.

How Zero-Config Autowiring Works

Rikta uses three mechanisms:

1. Automatic Discovery

At startup, Rikta scans your project for classes decorated with @Controller() or @Injectable(). It builds a dependency graph from constructor parameters and @Autowired() decorators.

2. Global Provider Registry

All providers live in a single registry. Any injectable class is available anywhere in your app. No need to export or import.

3. Dependency Resolution

Rikta reads TypeScript metadata (via reflect-metadata) to understand what each class needs. It creates instances in the correct order and injects them automatically.

The framework detects circular dependencies during initialization and throws a clear error:

Error: Circular dependency detected: 
UserService -> AuthService -> UserService

No cryptic stack traces. No runtime surprises.

A Side-by-Side Comparison

Creating a new feature in NestJS:

// 1. Create the service
@Injectable()
export class PaymentService {
  constructor(private configService: ConfigService) {}
}

// 2. Create the module
@Module({
  imports: [ConfigModule],
  providers: [PaymentService],
  exports: [PaymentService],
})
export class PaymentModule {}

// 3. Import in app.module.ts
@Module({
  imports: [PaymentModule, /* ... */],
})
export class AppModule {}

// 4. Import in any module that needs it
@Module({
  imports: [PaymentModule],
  providers: [OrderService],
})
export class OrderModule {}

Creating the same feature in Rikta:

@Injectable()
export class PaymentService {
  @Autowired()
  private configService!: ConfigService;
}

Done.

Performance Benefits

Rikta uses Fastify as its HTTP layer. Fastify handles up to 30,000 requests per second in benchmarks.

Benchmark results from the official repository:

Metric Rikta vs NestJS
Startup time 43% faster
GET requests 41% faster
POST requests 25% faster
Route parameters 46% faster

Rikta adds minimal overhead (2-5%) over vanilla Fastify. You get dependency injection and decorators without sacrificing speed.

Built-in Zod Validation

NestJS uses class-validator decorators for validation. This works, but you define types twice: once for TypeScript, once for validation.

Rikta integrates Zod natively. Define a schema once. Get validation and TypeScript types automatically.

import { Controller, Post, Body, z } from '@riktajs/core';

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
  age: z.number().optional(),
});

@Controller('/users')
export class UserController {
  @Post()
  create(@Body(CreateUserSchema) user: z.infer<typeof CreateUserSchema>) {
    // 'user' is validated AND typed
    // Invalid requests return 400 automatically
    return { created: user };
  }
}

No duplicate type definitions. No manual error handling for validation failures.

When To Use Rikta

Rikta works best for:

  • Startups and MVPs where development speed matters
  • Small to medium teams (1-15 developers)
  • Microservices where each service stays focused
  • Developers who know NestJS and want less boilerplate

Consider NestJS if:

  • You have a large team that needs strict module boundaries
  • You require the extensive NestJS ecosystem (specific adapters, plugins)
  • Your organization mandates explicit dependency documentation

Getting Started

Create a new project in seconds:

npx @riktajs/cli new my-app
cd my-app
npm run dev

Your API runs at http://localhost:3000.

The CLI generates a complete project with:

  • TypeScript configuration optimized for Rikta
  • Example controller with REST endpoints
  • Example service with dependency injection
  • Hot reload development server

Resources

Rikta is MIT licensed and open source.

Have you tried zero-config frameworks? What do you think about the module vs. autowiring tradeoff? Share your experience in the comments.

The Truth about Gemini 3: Why Google’s New AI Agents Win in 2026

2026-01-20 00:40:29

Liquid syntax error: Unknown tag 'endraw'

Advanced Notification System with Reapop and Redux in React

2026-01-20 00:35:29

Reapop is a powerful, Redux-based notification system for React applications. It provides a flexible and scalable approach to managing notifications using Redux for state management, making it ideal for complex applications that need centralized notification handling. This guide walks through advanced usage of Reapop with React and Redux, including custom configurations, middleware integration, and complex notification patterns. This is part 36 of a series on using Reapop with React.

Prerequisites

Before you begin, make sure you have:

  • Node.js version 14.0 or higher installed
  • npm, yarn, or pnpm package manager
  • A React project (version 16.8 or higher) or create-react-app setup
  • Redux and React-Redux installed and configured
  • Basic knowledge of Redux (actions, reducers, store)
  • Understanding of React hooks (useSelector, useDispatch)
  • Familiarity with JavaScript/TypeScript

Installation

Install Reapop and Redux dependencies:

npm install reapop react-redux redux

Or with yarn:

yarn add reapop react-redux redux

Or with pnpm:

pnpm add reapop react-redux redux

After installation, your package.json should include:

{
  "dependencies": {
    "reapop": "^4.0.0",
    "react-redux": "^8.0.0",
    "redux": "^4.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}

Project Setup

Set up Redux store with Reapop reducer. First, create the Redux store:

// src/store/index.js
import { createStore, combineReducers } from 'redux';
import { reducer as notificationsReducer } from 'reapop';

const rootReducer = combineReducers({
  notifications: notificationsReducer
});

export const store = createStore(rootReducer);

Now set up the provider and notification system in your main entry file:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './store';
import { NotificationsSystem } from 'reapop';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
      <NotificationsSystem />
    </Provider>
  </React.StrictMode>
);

First Example / Basic Usage

Let's create a simple notification component. Create a new file src/NotificationExample.jsx:

// src/NotificationExample.jsx
import React from 'react';
import { useDispatch } from 'react-redux';
import { notify } from 'reapop';

function NotificationExample() {
  const dispatch = useDispatch();

  const showSuccess = () => {
    dispatch(notify({
      title: 'Success!',
      message: 'Operation completed successfully.',
      status: 'success',
      dismissible: true,
      dismissAfter: 3000
    }));
  };

  const showError = () => {
    dispatch(notify({
      title: 'Error!',
      message: 'Something went wrong.',
      status: 'error',
      dismissible: true,
      dismissAfter: 5000
    }));
  };

  const showWarning = () => {
    dispatch(notify({
      title: 'Warning!',
      message: 'Please review your input.',
      status: 'warning',
      dismissible: true,
      dismissAfter: 4000
    }));
  };

  const showInfo = () => {
    dispatch(notify({
      title: 'Info',
      message: 'Here is some information.',
      status: 'info',
      dismissible: true,
      dismissAfter: 3000
    }));
  };

  return (
    <div style={{ padding: '20px' }}>
      <h2>Notification Examples</h2>
      <div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
        <button onClick={showSuccess}>Success</button>
        <button onClick={showError}>Error</button>
        <button onClick={showWarning}>Warning</button>
        <button onClick={showInfo}>Info</button>
      </div>
    </div>
  );
}

export default NotificationExample;

Update your App.jsx:

// src/App.jsx
import React from 'react';
import NotificationExample from './NotificationExample';
import './App.css';

function App() {
  return (
    <div className="App">
      <NotificationExample />
    </div>
  );
}

export default App;

Understanding the Basics

Reapop uses Redux for state management where:

  • notify action: Creates a new notification
  • dismissNotification action: Dismisses a specific notification
  • dismissNotifications action: Dismisses all notifications
  • NotificationsSystem: Component that renders all notifications
  • Redux Store: Centralized state management for all notifications

Key concepts for advanced usage:

  • Redux Integration: Notifications are managed through Redux store
  • Action Creators: Use notify action creator to create notifications
  • Middleware: Can integrate with Redux middleware for async operations
  • Custom Components: Customize notification appearance and behavior
  • Positioning: Configure notification positions and stacking

Here's an example with custom configuration:

// src/store/index.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { reducer as notificationsReducer, setUpNotifications } from 'reapop';
import thunk from 'redux-thunk';

// Configure notifications
setUpNotifications({
  defaultProps: {
    position: 'top-right',
    dismissible: true,
    dismissAfter: 5000
  }
});

const rootReducer = combineReducers({
  notifications: notificationsReducer
});

export const store = createStore(rootReducer, applyMiddleware(thunk));

Practical Example / Building Something Real

Let's build a comprehensive notification system with custom hooks, middleware integration, and advanced features:

// src/hooks/useNotifications.js
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { notify, dismissNotification } from 'reapop';

export const useNotifications = () => {
  const dispatch = useDispatch();

  const showSuccess = useCallback((title, message, options = {}) => {
    return dispatch(notify({
      title,
      message,
      status: 'success',
      dismissible: true,
      dismissAfter: 3000,
      ...options
    }));
  }, [dispatch]);

  const showError = useCallback((title, message, options = {}) => {
    return dispatch(notify({
      title,
      message,
      status: 'error',
      dismissible: true,
      dismissAfter: 5000,
      ...options
    }));
  }, [dispatch]);

  const showWarning = useCallback((title, message, options = {}) => {
    return dispatch(notify({
      title,
      message,
      status: 'warning',
      dismissible: true,
      dismissAfter: 4000,
      ...options
    }));
  }, [dispatch]);

  const showInfo = useCallback((title, message, options = {}) => {
    return dispatch(notify({
      title,
      message,
      status: 'info',
      dismissible: true,
      dismissAfter: 3000,
      ...options
    }));
  }, [dispatch]);

  const showLoading = useCallback((title, message) => {
    return dispatch(notify({
      title,
      message,
      status: 'loading',
      dismissible: false,
      dismissAfter: 0
    }));
  }, [dispatch]);

  const dismiss = useCallback((notificationId) => {
    dispatch(dismissNotification(notificationId));
  }, [dispatch]);

  return {
    showSuccess,
    showError,
    showWarning,
    showInfo,
    showLoading,
    dismiss
  };
};

Now create an advanced notification system component:

// src/AdvancedNotificationSystem.jsx
import React, { useState } from 'react';
import { useNotifications } from './hooks/useNotifications';
import { useDispatch } from 'react-redux';
import { dismissNotifications } from 'reapop';

function AdvancedNotificationSystem() {
  const { showSuccess, showError, showWarning, showInfo, showLoading, dismiss } = useNotifications();
  const dispatch = useDispatch();
  const [formData, setFormData] = useState({
    title: '',
    message: '',
    status: 'success'
  });

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({ ...prev, [name]: value }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!formData.title || !formData.message) {
      showError('Validation Error', 'Please fill in all fields.');
      return;
    }

    const options = {
      position: 'top-right',
      dismissible: true
    };

    switch (formData.status) {
      case 'success':
        showSuccess(formData.title, formData.message, options);
        break;
      case 'error':
        showError(formData.title, formData.message, options);
        break;
      case 'warning':
        showWarning(formData.title, formData.message, options);
        break;
      case 'info':
        showInfo(formData.title, formData.message, options);
        break;
      default:
        showInfo(formData.title, formData.message, options);
    }

    setFormData({ title: '', message: '', status: 'success' });
  };

  const simulateApiCall = async () => {
    const loadingId = showLoading('Processing', 'Processing your request...');

    try {
      // Simulate API call
      await new Promise(resolve => setTimeout(resolve, 2000));

      dismiss(loadingId);
      showSuccess('Success', 'Operation completed successfully!');
    } catch (error) {
      dismiss(loadingId);
      showError('Error', 'Failed to complete operation. Please try again.');
    }
  };

  const showActionNotification = () => {
    const notificationId = showWarning('Action Required', 'Do you want to proceed?', {
      dismissAfter: 0,
      buttons: [
        {
          name: 'Yes',
          primary: true,
          onClick: () => {
            dismiss(notificationId);
            showSuccess('Confirmed', 'Action confirmed.');
          }
        },
        {
          name: 'No',
          onClick: () => {
            dismiss(notificationId);
            showInfo('Cancelled', 'Action cancelled.');
          }
        }
      ]
    });
  };

  const clearAllNotifications = () => {
    dispatch(dismissNotifications());
  };

  return (
    <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
      <h1>Advanced Notification System</h1>

      <form onSubmit={handleSubmit} style={{ marginBottom: '20px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
        <div style={{ marginBottom: '16px' }}>
          <label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
            Title *
          </label>
          <input
            type="text"
            name="title"
            value={formData.title}
            onChange={handleInputChange}
            style={{
              width: '100%',
              padding: '8px',
              border: '1px solid #ddd',
              borderRadius: '4px',
              boxSizing: 'border-box'
            }}
            required
          />
        </div>

        <div style={{ marginBottom: '16px' }}>
          <label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
            Message *
          </label>
          <textarea
            name="message"
            value={formData.message}
            onChange={handleInputChange}
            rows={3}
            style={{
              width: '100%',
              padding: '8px',
              border: '1px solid #ddd',
              borderRadius: '4px',
              boxSizing: 'border-box',
              resize: 'vertical'
            }}
            required
          />
        </div>

        <div style={{ marginBottom: '16px' }}>
          <label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
            Status
          </label>
          <select
            name="status"
            value={formData.status}
            onChange={handleInputChange}
            style={{
              width: '100%',
              padding: '8px',
              border: '1px solid #ddd',
              borderRadius: '4px',
              boxSizing: 'border-box'
            }}
          >
            <option value="success">Success</option>
            <option value="error">Error</option>
            <option value="warning">Warning</option>
            <option value="info">Info</option>
          </select>
        </div>

        <button
          type="submit"
          style={{
            width: '100%',
            padding: '10px',
            backgroundColor: '#007bff',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer',
            fontSize: '16px'
          }}
        >
          Show Notification
        </button>
      </form>

      <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
        <button
          onClick={simulateApiCall}
          style={{
            padding: '10px',
            backgroundColor: '#28a745',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Simulate API Call
        </button>
        <button
          onClick={showActionNotification}
          style={{
            padding: '10px',
            backgroundColor: '#ffc107',
            color: 'black',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Show Action Notification
        </button>
        <button
          onClick={clearAllNotifications}
          style={{
            padding: '10px',
            backgroundColor: '#dc3545',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
          }}
        >
          Clear All Notifications
        </button>
      </div>
    </div>
  );
}

export default AdvancedNotificationSystem;

Update your App.jsx:

// src/App.jsx
import React from 'react';
import AdvancedNotificationSystem from './AdvancedNotificationSystem';
import './App.css';

function App() {
  return (
    <div className="App">
      <AdvancedNotificationSystem />
    </div>
  );
}

export default App;

Update your store configuration for better setup:

// src/store/index.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { reducer as notificationsReducer, setUpNotifications } from 'reapop';
import thunk from 'redux-thunk';

// Configure notifications
setUpNotifications({
  defaultProps: {
    position: 'top-right',
    dismissible: true,
    dismissAfter: 5000,
    allowHTML: false
  }
});

const rootReducer = combineReducers({
  notifications: notificationsReducer
});

export const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

Common Issues / Troubleshooting

  1. Notifications not displaying: Ensure NotificationsSystem is rendered in your app and Provider wraps your application with the Redux store.

  2. Redux store not configured: Make sure you've added the notifications reducer to your Redux store using combineReducers.

  3. Actions not working: Ensure you're using useDispatch from react-redux to dispatch notification actions. The notify action creator must be imported from reapop.

  4. Notifications not dismissing: Check the dismissAfter property. Set it to a number (milliseconds) for auto-dismissal, or 0 for persistent notifications. Use dismissible: true to allow manual dismissal.

  5. Custom styling not working: Reapop allows custom notification components. You can override the default notification component by configuring it in setUpNotifications.

  6. Multiple notifications stacking: Reapop handles multiple notifications automatically. They will stack based on the position setting. Use dismissNotifications() to clear all notifications at once.

Next Steps

Now that you have an advanced understanding of Reapop:

  • Explore Redux middleware integration for async notifications
  • Learn about custom notification components
  • Implement notification persistence and storage
  • Add notification queues and limits
  • Integrate with Redux DevTools for debugging
  • Learn about other notification libraries (notistack, react-toastify)
  • Check the official repository: https://github.com/LouisBarranqueiro/reapop
  • Look for part 37 of this series for more advanced topics

Summary

You've successfully integrated Reapop into your React application with Redux for advanced notification management. Reapop provides a scalable, Redux-based solution for displaying notifications with centralized state management and extensive customization options.

SEO Keywords

reapop
React Redux notifications
reapop tutorial
React notification system
reapop installation
React Redux toast
reapop example
React notification library
reapop setup
React Redux alerts
reapop customization
React notification hooks
reapop middleware
React notification state
reapop getting started

From Frequency Count to Two Pointer Technique: Level Up Your Array Skills 🚀

2026-01-20 00:35:03

We are familiar with frequency counting — counting elements in arrays or strings using hash maps or buckets. It’s a powerful tool, but as problems grow, relying solely on extra space can be limiting. That’s when the Two Pointer technique comes to the rescue: a space-efficient, intuitive way to traverse arrays or strings for many common problems.

In this article, we’ll explore what Two Pointer is, when to use it, types of problems it fits, and how to start practicing it today.

🔹 Frequency Count: The Warm-Up

Most beginner array problems use frequency counts:

vector<int> arr = {1,1,2,2,2,3};
unordered_map<int,int> freq;

for(int x : arr) freq[x]++;  // Count occurrences

for(auto [val, count] : freq) {
    cout << val << " appears " << count << " times\n";
}

This works great, but it uses extra space (O(n) for the map), and sometimes, we can solve problems in-place without extra memory using pointers.

That’s where Two Pointer technique shines.

🔹 What is Two Pointer Technique?

Two Pointer is using two indices instead of one to traverse an array, string, or linked list.

Why use it?

  • Reduce time complexity (often O(n² → O(n))
  • Avoid extra memory
  • Handle subarrays, pairs, or symmetrical checks efficiently

Think of it as moving pointers smartly based on conditions, instead of blindly iterating every element.

🔹 When to Think Two Pointer?
Ask yourself these questions:

✅ Is the problem on array / string / list?
✅ Is it sorted or can it be sorted?
✅ Are you asked about pairs, ranges, windows, or symmetry?
✅ Can you avoid nested loops with clever pointer movement?

If most answers are YES, Two Pointer is likely the right approach.

Keywords to spot:

“Two numbers sum to K” → left/right pointers

“Palindrome” → left/right pointers

“Remove duplicates” → slow/fast pointer

“Longest substring” → sliding window

“Merge arrays” → left/right pointers

🔹 Three Main Types of Two Pointer Problems

1️⃣ Opposite Direction (Left ↔ Right)

Pattern:

L →          ← R

Used for:

  • Sorted arrays
  • Symmetry or pair checks

Typical problems:

  • Two Sum (sorted array)
  • Palindrome check
  • Container With Most Water

Mental Rule:

  • Sum too small → move left
  • Sum too big → move right

Example:

int l = 0, r = n-1;
while(l < r) {
    int sum = arr[l] + arr[r];
    if(sum == target) return true;
    else if(sum < target) l++;
    else r--;
}

2️⃣ Same Direction (Slow & Fast)

Pattern:

slow →  
fast → →

Used for:

  • Modifying array/string in-place
  • Skipping or filtering elements

Typical problems:

  • Remove duplicates
  • Remove specific element
  • Move zeros
  • Compress string

Mental Rule:

  • Fast explores
  • Slow writes the valid elements

Example:

int slow = 0;
for(int fast = 0; fast < n; fast++) {
    if(arr[fast] != val) arr[slow++] = arr[fast];
}

3️⃣ Sliding Window (Expandable & Shrinkable)

Pattern:

L → → → R

Used for:

  • Subarrays or substrings
  • Conditions like sum, count, unique characters
  • Typical problems:
  • Longest substring without repeating characters
  • Minimum window substring
  • Subarray with sum ≤ K

Mental Rule:

  • Expand to include elements
  • Shrink to satisfy condition

🔹How to Identify Two Pointer Problems Quickly

Checklist:

  • Is it array/string?
  • Can you avoid nested loops?
  • Can pointer movement depend on condition?
  • Is sorting allowed?
  • Is answer based on pairs/range/window?

If 3+ YES, it’s likely a Two Pointer problem.

🔹Beginner Mistakes to Avoid

❌ Using pointers on unsorted arrays blindly
❌ Moving both pointers without logic
❌ Forgetting while(l < r) condition
❌ Confusing sliding window with opposite pointer approach

🔹How to Practice Effectively

  1. Identify the pattern first (no coding)
  2. Write pointer movement rules in plain English
  3. Code it
  4. Compare with solution
  5. Rewrite from memory

This reinforces logic, not just syntax.

🔹 Starter Practice Set

Try these in order:

  1. Reverse string
  2. Valid palindrome
  3. Two Sum (sorted array)
  4. Remove duplicates from sorted array
  5. Move zeros
  6. Container With Most Water
  7. Longest substring without repeating characters

🌱 Final Thought

Two Pointer isn’t about syntax.
It’s about smart pointer movement:

“If condition improves → move this pointer
If condition worsens → move the other pointer”

Master this, and many array problems become simple, elegant, and efficient.

[Looking 4 Feedback] I wanted a better way to handle routines in Python, so I built processes lib

2026-01-20 00:32:46

Hi everyone!

I’ve been working on a library called processes. It’s designed to simplify and "robustify" periodic routines.

Description: You create a Process made out of Tasks. Tasks can have dependencies and can run in parallel. If one fails, non-dependent Tasks continue to run. If set, en email notification is sent on failed Tasks giving details of the error and all Tasks that could not be executed.

Code example:

from datetime import date

from processes import Process, Task, TaskDependency, HTMLSMTPHandler

# 1. Setup Email Alerts (Optional)
smtp_handler = HTMLSMTPHandler(
    ('smtp_server', 587), '[email protected]', ['[email protected]', '[email protected]'], 
    use_tls=True, credentials=('user', 'pass')
)

# 2. If necessary, create wrappers for your Tasks.
def get_previous_working_day():
    return date(2025, 12, 30)
def indep_task():
    return "foo"
def search_and_sum_csv(t: date):
    return 10
def sum_data_from_csv_and_x(x, a=1, b=2):
    return x + a + b

# 3. Create the Task Graph (order is irrelevant, that is handled by Process)
tasks = [
    Task("t-1", "etl.log", get_previous_working_day),
    Task("intependent", "indep.log", indep_task, html_mail_handler=smtp_handler),  # This task will send email on failure
    Task("sum_csv", "etl.log", search_and_sum_csv,
            dependencies= [
                TaskDependency("t-1",
                use_result_as_additional_args=True)  # Adds result of t-1 task to search_and_sum_csv function as aditional args
            ]
        ),
    Task("sum_x_and_csv", "etl.log", sum_data_from_csv_and_x,
            args = (10,), kwargs = {"b": 100},
            dependencies=[
                TaskDependency("sum_csv",
            use_result_as_additional_kwargs=True,
            additional_kwarg_name="a")
        ]
    )
]

# 4. Run the Process
with Process(tasks) as process: # Context Manager ensures correct disposal of loggers
    process_result = process.run() # To enable parallelization use .run(parallel=True)

I’ve spent the last few days setting up a "pro" workflow (uv, ruff, mypy, pytest, and GitHub Actions with Trusted Publishers), and I’ve finally hit PyPI.

I’m looking for feedback on:

  • Functionality: Would you add anything?
  • Coding Style
  • Project structure (examples, tests, pyproject.toml)
  • CI/CD: github workflows

Repo: https://github.com/oliverm91/processes
PyPI: https://pypi.org/project/processes/

Thanks in advance for any roasting or advice you can give!