← Back to Demo Home

đŸ”Ĩ ModernToasts + Svelte

Complete Svelte integration with stores and reactive statements

🚀 Live Demo

Try the Svelte integration in action:

đŸ“Ļ Installation

npm install modern-toasts

đŸŽ¯ Basic Usage

Simple Import and Use

<script>
  import toast from 'modern-toasts';

  function handleClick() {
    toast.success('Hello from Svelte!');
  }
</script>

<button on:click={handleClick}>
  Show Toast
</button>

đŸĒ Toast Store

Create a Svelte store for toast management

// stores/toast.js
import { writable } from 'svelte/store';
import toast from 'modern-toasts';

// Configure toast on import
toast.configure({
  position: 'bottom-right',
  maxVisibleStackToasts: 3,
  enableBorderAnimation: true,
  enableFillAnimation: true,
  pauseBackgroundToastsOnHover: true
});

// Create stores for toast state
export const toastConfig = writable({
  position: 'bottom-right',
  maxVisible: 3,
  enableAnimations: true
});

export const activeToasts = writable([]);

// Toast methods
export const toastService = {
  success: (message, options = {}) => {
    const id = toast.success(message, options);
    activeToasts.update(toasts => [...toasts, { id, type: 'success', message }]);
    return id;
  },

  error: (message, options = {}) => {
    const id = toast.error(message, options);
    activeToasts.update(toasts => [...toasts, { id, type: 'error', message }]);
    return id;
  },

  info: (message, options = {}) => {
    const id = toast.info(message, options);
    activeToasts.update(toasts => [...toasts, { id, type: 'info', message }]);
    return id;
  },

  warning: (message, options = {}) => {
    const id = toast.warning(message, options);
    activeToasts.update(toasts => [...toasts, { id, type: 'warning', message }]);
    return id;
  },

  dismiss: (id) => {
    toast.dismiss(id);
    activeToasts.update(toasts => toasts.filter(t => t.id !== id));
  },

  dismissAll: () => {
    toast.dismissAll();
    activeToasts.set([]);
  },

  configure: (config) => {
    toast.configure(config);
    toastConfig.update(current => ({ ...current, ...config }));
  }
};

// Listen to toast events to update store
toast.on('dismiss', (toastData) => {
  activeToasts.update(toasts => toasts.filter(t => t.id !== toastData.id));
});

Use in components

<script>
  import { toastService, activeToasts } from '../stores/toast.js';

  async function saveData() {
    try {
      await api.saveData();
      toastService.success('Data saved successfully!');
    } catch (error) {
      toastService.error('Failed to save data');
    }
  }
</script>

<button on:click={saveData}>Save Data</button>

{#if $activeToasts.length > 0}
  <p>Active toasts: {$activeToasts.length}</p>
{/if}

⚡ Reactive Statements

Reactive toast configuration

<script>
  import { toastService, toastConfig } from '../stores/toast.js';

  let position = 'bottom-right';
  let maxVisible = 3;
  let enableAnimations = true;

  // Reactive statement to update configuration
  $: {
    toastService.configure({
      position,
      maxVisibleStackToasts: maxVisible,
      enableBorderAnimation: enableAnimations,
      enableFillAnimation: enableAnimations
    });
  }

  // Reactive toast based on data changes
  let userData = {};
  $: if (userData.name) {
    toastService.info(`Welcome, ${userData.name}!`);
  }
</script>

<select bind:value={position}>
  <option value="top-left">Top Left</option>
  <option value="top-right">Top Right</option>
  <option value="bottom-left">Bottom Left</option>
  <option value="bottom-right">Bottom Right</option>
</select>

<input type="range" bind:value={maxVisible} min="1" max="5">
<label>Max Visible: {maxVisible}</label>

<input type="checkbox" bind:checked={enableAnimations}>
<label>Enable Animations</label>

📝 Form Integration

Two-way binding with validation

<script>
  import { toastService } from '../stores/toast.js';

  let formData = {
    email: '',
    password: '',
    confirmPassword: ''
  };

  let errors = {};

  // Reactive validation
  $: {
    errors = {};
    
    if (formData.email && !isValidEmail(formData.email)) {
      errors.email = 'Invalid email format';
    }
    
    if (formData.password && formData.password.length < 8) {
      errors.password = 'Password must be at least 8 characters';
    }
    
    if (formData.confirmPassword && formData.password !== formData.confirmPassword) {
      errors.confirmPassword = 'Passwords do not match';
    }
  }

  function isValidEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  }

  async function handleSubmit() {
    // Show validation errors as toasts
    if (Object.keys(errors).length > 0) {
      Object.values(errors).forEach(error => {
        toastService.error(error);
      });
      return;
    }

    try {
      await submitForm(formData);
      toastService.success('Account created successfully!', {
        autoDismiss: 5000
      });
      
      // Reset form
      formData = { email: '', password: '', confirmPassword: '' };
    } catch (error) {
      toastService.error('Failed to create account');
    }
  }
</script>

<form on:submit|preventDefault={handleSubmit}>
  <input 
    type="email" 
    bind:value={formData.email} 
    placeholder="Email"
    class:error={errors.email}
  >
  
  <input 
    type="password" 
    bind:value={formData.password} 
    placeholder="Password"
    class:error={errors.password}
  >
  
  <input 
    type="password" 
    bind:value={formData.confirmPassword} 
    placeholder="Confirm Password"
    class:error={errors.confirmPassword}
  >
  
  <button type="submit" disabled={Object.keys(errors).length > 0}>
    Create Account
  </button>
</form>

🔄 Real-world Examples

API Integration with Loading States

<script>
  import { toastService } from '../stores/toast.js';
  import { onMount } from 'svelte';

  let loading = false;
  let data = null;
  let error = null;

  async function fetchData() {
    loading = true;
    error = null;
    
    const loadingToastId = toastService.info('Loading data...', {
      autoDismiss: 0
    });

    try {
      const response = await fetch('/api/data');
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      
      data = await response.json();
      
      toastService.dismiss(loadingToastId);
      toastService.success('Data loaded successfully!');
      
    } catch (err) {
      error = err.message;
      toastService.dismiss(loadingToastId);
      
      if (err.message.includes('404')) {
        toastService.error('Data not found');
      } else if (err.message.includes('500')) {
        toastService.error('Server error. Please try again.');
      } else {
        toastService.error('Network error occurred');
      }
    } finally {
      loading = false;
    }
  }

  // Auto-fetch on mount
  onMount(fetchData);
</script>

<button on:click={fetchData} disabled={loading}>
  {loading ? 'Loading...' : 'Refresh Data'}
</button>

{#if data}
  <div>Data loaded: {JSON.stringify(data)}</div>
{/if}

User Actions with Feedback

<script>
  import { toastService } from '../stores/toast.js';

  let liked = false;
  let bookmarked = false;

  function toggleLike() {
    liked = !liked;
    
    if (liked) {
      toastService.success('â¤ī¸ Added to favorites!', {
        autoDismiss: 2000
      });
    } else {
      toastService.info('💔 Removed from favorites', {
        autoDismiss: 2000
      });
    }
  }

  function toggleBookmark() {
    bookmarked = !bookmarked;
    
    if (bookmarked) {
      toastService.info('🔖 Bookmarked!', {
        autoDismiss: 2000
      });
    } else {
      toastService.info('📖 Bookmark removed', {
        autoDismiss: 2000
      });
    }
  }

  async function shareContent() {
    try {
      await navigator.clipboard.writeText(window.location.href);
      toastService.success('🔗 Link copied to clipboard!');
    } catch (error) {
      toastService.error('Failed to copy link');
    }
  }
</script>

<button on:click={toggleLike} class:liked>
  {liked ? 'â¤ī¸' : '🤍'} Like
</button>

<button on:click={toggleBookmark} class:bookmarked>
  {bookmarked ? '🔖' : '📖'} Bookmark
</button>

<button on:click={shareContent}>
  🔗 Share
</button>

🎭 Custom Toast Component

<!-- ToastTrigger.svelte -->
<script>
  import { toastService } from '../stores/toast.js';

  export let type = 'info';
  export let message = '';
  export let label = 'Show Toast';
  export let options = {};
  export let disabled = false;

  function triggerToast() {
    const toastMessage = message || `${type} toast triggered!`;
    toastService[type](toastMessage, options);
  }
</script>

<button 
  class="btn btn-{type}" 
  on:click={triggerToast}
  {disabled}
>
  {label}
</button>

<style>
  .btn {
    padding: 0.75rem 1.5rem;
    border: none;
    border-radius: 8px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s ease;
    color: white;
  }

  .btn:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  }

  .btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .btn-success { background: #10b981; }
  .btn-error { background: #ef4444; }
  .btn-info { background: #3b82f6; }
  .btn-warning { background: #f59e0b; }
</style>

Usage in other components

<script>
  import ToastTrigger from './ToastTrigger.svelte';
</script>

<ToastTrigger 
  type="success"
  message="Operation completed successfully!"
  label="Complete Task"
  options={{ autoDismiss: 3000 }}
/>

<ToastTrigger 
  type="error"
  message="Something went wrong!"
  label="Trigger Error"
/>

âš™ī¸ Advanced Configuration

<!-- App.svelte -->
<script>
  import { onMount } from 'svelte';
  import { toastService } from './stores/toast.js';

  onMount(() => {
    // Configure toast globally
    toastService.configure({
      position: 'top-right',
      maxVisibleStackToasts: 4,
      defaultDuration: 4000,
      enableBorderAnimation: true,
      enableFillAnimation: true,
      pauseBackgroundToastsOnHover: true,
      customCSS: `
        .toast-container {
          z-index: 9999;
        }
        .toast-svelte {
          border-left: 4px solid #ff3e00;
        }
      `
    });
  });
</script>

<main>
  <!-- Your app content -->
</main>

📱 TypeScript Support

// stores/toast.ts
import { writable, type Writable } from 'svelte/store';
import toast, { type ToastOptions, type ToastType } from 'modern-toasts';

interface CustomToastOptions extends ToastOptions {
  userId?: string;
  component?: string;
}

interface ToastState {
  id: string;
  type: ToastType;
  message: string;
  timestamp: number;
}

export const activeToasts: Writable<ToastState[]> = writable([]);

export const toastService = {
  success: (message: string, options?: CustomToastOptions): string => {
    const id = toast.success(message, options);
    activeToasts.update(toasts => [...toasts, {
      id, type: 'success', message, timestamp: Date.now()
    }]);
    return id;
  },

  // ... other typed methods
};

🔗 Useful Links