Skip to main content

Plugin Initialization

Plugin initialization allows your plugin to perform asynchronous operations before it becomes ready to use. This is essential for plugins that need to load configuration, fetch data, or perform setup tasks.

Prerequisites

You have created a Strapi plugin and understand the Admin Panel API.

What is Plugin Initialization?

Plugin initialization is controlled by two key parameters in registerPlugin:

  • initializer: A React component that handles async setup
  • isReady: A boolean flag indicating plugin readiness (default: true)
app.registerPlugin({
id: 'my-plugin',
name: 'My Plugin',
initializer: MyInitializerComponent,
isReady: false, // Plugin starts as not ready
});

Use Case: Configuration Loading Plugin

Let's build a Configuration Plugin that loads settings from an external API before becoming ready.

1. Creating the Initializer Component

// src/plugins/configuration/admin/src/components/Initializer.tsx
import React from 'react';

interface InitializerProps {
setPlugin: (pluginId: string) => void;
}

const Initializer: React.FC<InitializerProps> = ({ setPlugin }) => {
const [status, setStatus] = React.useState('Loading configuration...');

React.useEffect(() => {
const initializePlugin = async () => {
try {
setStatus('Fetching configuration from API...');

// Simulate API call to load configuration
const response = await fetch('/api/plugin-configuration');
const config = await response.json();

setStatus('Validating configuration...');

// Validate configuration
if (!config.apiKey || !config.endpoint) {
throw new Error('Invalid configuration: missing required fields');
}

setStatus('Storing configuration...');

// Store configuration in plugin state
localStorage.setItem('plugin-configuration', JSON.stringify(config));

setStatus('Configuration loaded successfully!');

// Mark plugin as ready
setPlugin('configuration');
} catch (error) {
console.error('Failed to initialize configuration plugin:', error);
setStatus(`Error: ${error.message}`);

// Still mark as ready to prevent hanging
setTimeout(() => {
setPlugin('configuration');
}, 3000);
}
};

initializePlugin();
}, [setPlugin]);

return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Configuration Plugin</h3>
<p>{status}</p>
<div style={{ marginTop: '10px' }}>
<div
style={{
width: '200px',
height: '4px',
backgroundColor: '#f0f0f0',
borderRadius: '2px',
overflow: 'hidden',
}}
>
<div
style={{
width: '100%',
height: '100%',
backgroundColor: '#007bff',
animation: 'loading 2s ease-in-out infinite',
}}
/>
</div>
</div>
</div>
);
};

export default Initializer;

2. Registering the Plugin with Initializer

// src/plugins/configuration/admin/src/index.ts
import Initializer from './components/Initializer';

export default {
register(app: any) {
app.registerPlugin({
id: 'configuration',
name: 'Configuration',
initializer: Initializer,
isReady: false, // Plugin needs initialization
});
},
};

3. Using the Initialized Plugin

// In another plugin or component
const useConfiguration = () => {
const [config, setConfig] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(true);

React.useEffect(() => {
const checkConfiguration = () => {
const storedConfig = localStorage.getItem('plugin-configuration');
if (storedConfig) {
setConfig(JSON.parse(storedConfig));
setIsLoading(false);
} else {
// Configuration not ready yet
setTimeout(checkConfiguration, 1000);
}
};

checkConfiguration();
}, []);

return { config, isLoading };
};

Advanced Initialization Patterns

1. Multi-Step Initialization

// src/plugins/advanced-plugin/admin/src/components/Initializer.tsx
const AdvancedInitializer: React.FC<InitializerProps> = ({ setPlugin }) => {
const [currentStep, setCurrentStep] = React.useState(0);
const [error, setError] = React.useState(null);

const steps = [
'Loading user preferences...',
'Fetching API credentials...',
'Validating permissions...',
'Setting up data connections...',
'Initializing components...',
];

React.useEffect(() => {
const initializeStepByStep = async () => {
try {
// Step 1: Load user preferences
setCurrentStep(0);
await loadUserPreferences();

// Step 2: Fetch API credentials
setCurrentStep(1);
await fetchApiCredentials();

// Step 3: Validate permissions
setCurrentStep(2);
await validatePermissions();

// Step 4: Setup data connections
setCurrentStep(3);
await setupDataConnections();

// Step 5: Initialize components
setCurrentStep(4);
await initializeComponents();

// All steps completed
setPlugin('advanced-plugin');
} catch (error) {
setError(error.message);
console.error('Initialization failed at step:', currentStep, error);
}
};

initializeStepByStep();
}, [setPlugin, currentStep]);

if (error) {
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Initialization Failed</h3>
<p style={{ color: 'red' }}>{error}</p>
<button onClick={() => window.location.reload()}>Retry</button>
</div>
);
}

return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Advanced Plugin</h3>
<p>{steps[currentStep]}</p>
<div style={{ marginTop: '10px' }}>
<div
style={{
width: '200px',
height: '4px',
backgroundColor: '#f0f0f0',
borderRadius: '2px',
}}
>
<div
style={{
width: `${((currentStep + 1) / steps.length) * 100}%`,
height: '100%',
backgroundColor: '#007bff',
transition: 'width 0.3s ease',
}}
/>
</div>
<p style={{ fontSize: '12px', marginTop: '5px' }}>
Step {currentStep + 1} of {steps.length}
</p>
</div>
</div>
);
};

2. Conditional Initialization

// src/plugins/conditional-plugin/admin/src/components/Initializer.tsx
const ConditionalInitializer: React.FC<InitializerProps> = ({ setPlugin }) => {
React.useEffect(() => {
const initializeConditionally = async () => {
// Check if plugin should be initialized
const shouldInitialize = await checkInitializationRequirements();

if (shouldInitialize) {
// Perform full initialization
await loadFullConfiguration();
await setupAdvancedFeatures();
} else {
// Use minimal configuration
await loadMinimalConfiguration();
}

setPlugin('conditional-plugin');
};

initializeConditionally();
}, [setPlugin]);

return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Conditional Plugin</h3>
<p>Checking requirements...</p>
</div>
);
};

3. Timeout and Retry Logic

// src/plugins/resilient-plugin/admin/src/components/Initializer.tsx
const ResilientInitializer: React.FC<InitializerProps> = ({ setPlugin }) => {
const [retryCount, setRetryCount] = React.useState(0);
const maxRetries = 3;

React.useEffect(() => {
const initializeWithRetry = async () => {
try {
// Set timeout for initialization
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Initialization timeout')), 10000);
});

const initPromise = performInitialization();

await Promise.race([initPromise, timeoutPromise]);
setPlugin('resilient-plugin');
} catch (error) {
console.error(
`Initialization attempt ${retryCount + 1} failed:`,
error
);

if (retryCount < maxRetries) {
setRetryCount((prev) => prev + 1);
// Retry after delay
setTimeout(initializeWithRetry, 2000 * (retryCount + 1));
} else {
// Max retries reached, mark as ready anyway
console.warn('Max retries reached, marking plugin as ready');
setPlugin('resilient-plugin');
}
}
};

initializeWithRetry();
}, [setPlugin, retryCount]);

return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h3>Resilient Plugin</h3>
<p>
{retryCount === 0
? 'Initializing...'
: `Retrying... (${retryCount}/${maxRetries})`}
</p>
</div>
);
};

Best Practices

1. Always Call setPlugin

// ✅ Good - Always calls setPlugin
React.useEffect(() => {
const initialize = async () => {
try {
await loadData();
setPlugin('my-plugin'); // Success case
} catch (error) {
console.error(error);
setPlugin('my-plugin'); // Error case - still mark as ready
}
};
initialize();
}, [setPlugin]);

// ❌ Bad - setPlugin might not be called
React.useEffect(() => {
const initialize = async () => {
await loadData(); // If this throws, setPlugin is never called
setPlugin('my-plugin');
};
initialize();
}, [setPlugin]);

2. Provide User Feedback

// ✅ Good - Clear status messages
const [status, setStatus] = React.useState('Loading...');

const initialize = async () => {
setStatus('Connecting to server...');
await connectToServer();

setStatus('Loading configuration...');
await loadConfiguration();

setStatus('Ready!');
setPlugin('my-plugin');
};

3. Handle Errors Gracefully

// ✅ Good - Graceful error handling
const initialize = async () => {
try {
await loadCriticalData();
setPlugin('my-plugin');
} catch (error) {
console.error('Critical initialization failed:', error);
// Still mark as ready to prevent hanging
setPlugin('my-plugin');
}
};

4. Use Timeouts

// ✅ Good - Prevent infinite loading
const initialize = async () => {
const timeout = setTimeout(() => {
console.warn('Initialization timeout, marking as ready');
setPlugin('my-plugin');
}, 30000); // 30 second timeout

try {
await loadData();
clearTimeout(timeout);
setPlugin('my-plugin');
} catch (error) {
clearTimeout(timeout);
setPlugin('my-plugin');
}
};

Common Patterns

1. Configuration Loading

const ConfigInitializer: React.FC<InitializerProps> = ({ setPlugin }) => {
React.useEffect(() => {
const loadConfig = async () => {
const config = await fetch('/api/plugin-config').then((r) => r.json());
window.pluginConfig = config;
setPlugin('config-plugin');
};
loadConfig();
}, [setPlugin]);

return <div>Loading configuration...</div>;
};

2. Data Preloading

const DataInitializer: React.FC<InitializerProps> = ({ setPlugin }) => {
React.useEffect(() => {
const preloadData = async () => {
const [users, settings, permissions] = await Promise.all([
fetch('/api/users').then((r) => r.json()),
fetch('/api/settings').then((r) => r.json()),
fetch('/api/permissions').then((r) => r.json()),
]);

// Store in global state or context
window.preloadedData = { users, settings, permissions };
setPlugin('data-plugin');
};
preloadData();
}, [setPlugin]);

return <div>Preloading data...</div>;
};

Troubleshooting

Common Issues

  1. Plugin never becomes ready

    • Ensure setPlugin is always called
    • Check for unhandled promise rejections
    • Add timeout protection
  2. Initialization hangs

    • Add timeout mechanisms
    • Check for infinite loops
    • Verify API endpoints are accessible
  3. Error handling

    • Always call setPlugin even on error
    • Provide meaningful error messages
    • Consider retry logic for transient failures

Debugging Tips

// Add debugging to your initializer
const DebugInitializer: React.FC<InitializerProps> = ({ setPlugin }) => {
React.useEffect(() => {
console.log('Initializer started');

const initialize = async () => {
try {
console.log('Step 1: Loading data...');
await loadData();

console.log('Step 2: Processing...');
await processData();

console.log('Step 3: Ready!');
setPlugin('debug-plugin');
} catch (error) {
console.error('Initialization error:', error);
setPlugin('debug-plugin');
}
};

initialize();
}, [setPlugin]);

return <div>Debug: Initializing...</div>;
};

Next Steps