This is a very important architectural decision in Gutenberg — and honestly, many developers misuse both.
Let’s break it down in a practical, real-world way so you can decide like a tech lead 👇
🧠 select('core') vs apiFetch — What’s the Difference?
| Feature | select('core') | apiFetch |
|---|---|---|
| Data Source | WP Data Store (core-data) | Direct REST API |
| Reactivity | ✅ Reactive (auto re-render) | ❌ Not reactive |
| Caching | ✅ Built-in caching | ❌ No caching |
| Performance | ✅ Optimized | ⚠️ Depends on usage |
| Ease of Use | ✅ Cleaner in blocks | ⚠️ More manual |
| Control | ⚠️ Limited | ✅ Full control |
✅ When You Should Use select('core') (Recommended Most of the Time)
👉 Use this inside Gutenberg blocks/UI
✔ Best for:
- Fetching posts, pages, users
- Displaying dynamic data in UI
- When you want automatic updates
- When performance matters
Example
const posts = useSelect((select) => {
return select('core').getEntityRecords('postType', 'post');
}, []);
💡 Why it’s better
- Uses centralized state (Redux-like store)
- Avoids duplicate API calls
- Auto re-renders UI when data changes
- Works seamlessly with Gutenberg
⚠️ When apiFetch is Better
👉 Use this when you need full control or custom behavior
✔ Best for:
- Custom REST API endpoints
- POST / PUT / DELETE requests
- Non-Gutenberg environments
- One-time operations (form submit, migration scripts, etc.)
Example
import apiFetch from '@wordpress/api-fetch';const fetchData = async () => {
const posts = await apiFetch({
path: '/wp/v2/posts?per_page=5',
}); console.log(posts);
};
🚨 Common Mistake (Very Important)
❌ Using apiFetch inside React render logic:
// ❌ BAD PRACTICE
const posts = await apiFetch({ path: '/wp/v2/posts' });
👉 This causes:
- Multiple API calls
- No caching
- Performance issues
⚡ Real-World Decision Rule (Use This)
👉 Ask yourself:
1. Is this UI data inside Gutenberg?
👉 ✅ Use select('core')
2. Is this a custom API or action (POST/UPDATE)?
👉 ✅ Use apiFetch
3. Do I need caching + reactivity?
👉 ✅ Use select('core')
4. Do I need full control over request?
👉 ✅ Use apiFetch
🏆 Best Practice (Senior-Level Pattern)
👉 Combine both when needed:
- Use
select('core')→ for reading data - Use
apiFetch→ for writing/updating data
Example Pattern
// Read (cached + reactive)
const posts = useSelect((select) =>
select('core').getEntityRecords('postType', 'post')
);// Write (manual control)
const createPost = async () => {
await apiFetch({
path: '/wp/v2/posts',
method: 'POST',
data: { title: 'New Post' },
});
};
🧩 Bonus Insight (Interview-Level)
👉 select('core') internally:
- Uses @wordpress/core-data
- Wraps REST API calls
- Adds caching + resolution tracking
- Prevents duplicate requests
🏁 Final Verdict
👉 If you’re building Gutenberg blocks:
Use
select('core')80–90% of the time
👉 Use apiFetch only when:
- You need custom logic
- Or you’re mutating data