كيفية بناء نظام Feature Toggle في Next.js وReact خلال أقل من 30 دقيقة
مقدمة: لماذا تحتاج إلى Feature Toggle في تطبيقات الويب؟
يُعد Feature Toggle، ويُعرف أيضاً باسم Feature Flag، من الأساليب العملية التي تمنح فرق التطوير القدرة على تشغيل الميزات أو إخفائها دون الحاجة إلى تعديل الشيفرة البرمجية في كل مرة. هذه الفكرة مفيدة جداً عند تطوير خصائص جديدة لم تكتمل بعد، أو عند الرغبة في إطلاق ميزة تدريجياً، أو حتى عند تخصيص عناصر موسمية داخل واجهة الموقع.
في هذا الدليل سنبني نظاماً بسيطاً وفعالاً لإدارة الميزات داخل تطبيق مبني باستخدام Next.js وReact وTypeScript. كما سنعتمد على متغيرات البيئة عبر Environment Variables، ثم ننشر التطبيق على منصة Vercel لمحاكاة سيناريو واقعي قريب من بيئات الإنتاج.
![]()
ما الذي ستتعلمه في هذا المقال؟
- فهم مفهوم
Feature Togglesواستخداماته العملية. - العمل مع
React Context APIلتمرير البيانات عبر مكونات التطبيق. - استخدام
Environment Variablesللتحكم في الميزات دون تعديل الكود. - إنشاء
Custom HookفيReactلاستهلاك حالة الميزة بسهولة. - ربط النظام بواجهة
APIداخلNext.js. - نشر المشروع على
Vercelوتفعيل الميزات من لوحة الإعدادات.
ما هو نظام Feature Toggle؟
نظام Feature Toggle هو آلية تسمح لك بتغيير سلوك التطبيق اعتماداً على إعدادات خارجية، بدلاً من تعديل الشيفرة في كل مرة. على سبيل المثال، قد ترغب شركة ما في تغيير شعارها خلال موسم الأعياد عبر إضافة قبعة أو تأثير بصري معين. بدلاً من إعادة تطوير الصفحة سنوياً، يمكنها تجهيز هذا التغيير مرة واحدة وربطه بمفتاح تفعيل يمكن تشغيله عند الحاجة.
وهناك حالة أكثر أهمية في بيئات التطوير الحديثة: عندما يعمل الفريق وفق مبدأ Continuous Integration (CI)، يمكن دمج كود ميزة غير مكتملة داخل الفرع الرئيسي، ثم تعطيلها في بيئة الإنتاج حتى تصبح جاهزة للمستخدمين. بهذه الطريقة يستمر التطوير بوتيرة سريعة دون المخاطرة بعرض وظائف ناقصة.
متى يكون هذا الأسلوب مفيداً؟
- عند إطلاق ميزات جديدة على مراحل.
- عند اختبار خصائص داخلية قبل إتاحتها للجمهور.
- عند إخفاء أجزاء غير مكتملة من التطبيق.
- عند تنفيذ تغييرات موسمية أو ترويجية مؤقتة.
- عند تقليل المخاطر أثناء النشر إلى الإنتاج.
المتطلبات الأساسية قبل البدء
يفضل أن تكون لديك معرفة أساسية بـ Node.js وReact، بالإضافة إلى استخدام Git وGitHub. في هذا الشرح لن نتوسع في إنشاء المشروع من الصفر، بل سننطلق من بنية جاهزة ونركز على بناء آلية التفعيل نفسها.
الفكرة الأساسية للتطبيق التجريبي هي عرض مخططين ماليين:
- المخطط الأول يعرض بيانات
GDPالخاصة بالولايات المتحدة. - المخطط الثاني يعرض بيانات
Treasury Constant Maturityلآخر عشر سنوات.
المطلوب هو إخفاء المخطط الثاني خلف Feature Toggle حتى نستطيع تشغيله أو تعطيله في الوقت المناسب.

بدء المشروع وتشغيله محلياً
أول خطوة هي استنساخ المشروع التجريبي عبر Git:
$ git clone git@github.com:mateuszsokola/next-feature-toggle-scaffolder.git
بعد ذلك ثبّت الاعتماديات وشغّل الخادم المحلي:
$ cd next-feature-toggle-scaffolder
$ npm install
$ npm run dev
# COMMAND OUTPUT:
> next-feature-toggle-example@0.1.0 dev /Users/msokola/code/next-feature-toggle-scaffolder
> next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info - Using webpack 5. Reason: no next.config.js
https://nextjs.org/docs/messages/webpack5
event - compiled successfully
event - build page: /
wait - compiling...
event - compiled successfully
بعد تشغيل المشروع، افتح الرابط http://localhost:3000/ في المتصفح.

لماذا نستخدم React Context API؟
أسهل طريقة لبناء نظام مركزي للميزات في هذا المشروع هي استخدام React Context API. يتيح هذا الأسلوب تمرير البيانات بين المكونات دون الحاجة إلى إرسالها يدوياً عبر كل طبقة من طبقات الواجهة.
من دون Context قد تجد نفسك مضطراً لتمرير خاصية مثل enabledFeatures من مكون إلى آخر حتى تصل إلى الجزء الذي يحتاجها فعلاً، وهذا يزيد التعقيد ويرفع احتمالات الخطأ.
<Page enabledFeatures={features} />
<PageLayout enabledFeatures={features} />
<NavigationBar enabledFeatures={features} />
<Link href="https://freecodecamp.org/news/">
<Avatar enabledFeatures={features} />
</Link>
أما باستخدام Context فيمكنك حقن البيانات مباشرة في المكون الذي يحتاجها فقط.
إنشاء Context لإدارة الميزات
أنشئ مجلداً باسم context/ ثم ملفاً جديداً باسم FeatureToggleContext.ts:
mkdir context
cd context
touch FeatureToggleContext.ts
ثم أضف الكود التالي:
// file: context/FeatureToggleContext.ts
import React from "react";
export const FeatureToggleContext = React.createContext({
enabledFeatures: [] as string[],
});
القيمة الافتراضية هنا تُستخدم فقط عندما لا يكون المكون مغلفاً بالمزوّد Provider. لذلك ينبغي اعتبارها قيمة احتياطية وليست مصدراً فعلياً للبيانات.
إنشاء مكوّن Provider مخصص
الخطوة التالية هي إنشاء مكوّن يغلّف التطبيق ويوفر له قائمة الميزات النشطة. أنشئ ملفاً داخل مجلد components/ باسم FeatureToggle.tsx:
cd components
touch FeatureToggle.tsx
ثم استخدم الكود التالي:
// file: components/FeatureToggle.tsx
import React from "react";
import { FeatureToggleContext } from "../context/FeatureToggleContext";
type Props = {
children: any;
enabledFeatures: string[];
};
export const FeatureToggle = ({ children, enabledFeatures }: Props) => {
return (
<FeatureToggleContext.Provider value={{ enabledFeatures }}>
{children}
</FeatureToggleContext.Provider>
);
};
هذا المكوّن يستقبل خاصيتين:
children: وهي محتويات التطبيق.enabledFeatures: وهي قائمة الميزات المفعلة حالياً.
ربط Provider بالمكوّن الرئيسي في Next.js
في تطبيقات Next.js يكون الملف المناسب لتغليف التطبيق بالكامل هو pages/_app.tsx. في البداية سنضيف قائمة الميزات بشكل مباشر للتأكد من أن كل شيء يعمل:
import { FeatureToggle } from "../components/FeatureToggle";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
const enabledFeatures = ["treasury_chart"];
return (
<FeatureToggle enabledFeatures={enabledFeatures}>
<Component {...pageProps} />
</FeatureToggle>
);
}
export default MyApp;
بهذا أصبحت قائمة الميزات متاحة على مستوى التطبيق كله. لكننا ما زلنا بحاجة إلى طريقة سهلة لاستهلاك هذه البيانات داخل الصفحات والمكونات.
إنشاء Custom Hook لاستهلاك حالة الميزة
أفضل أسلوب عملي هنا هو كتابة Custom Hook يقرأ القيم من Context ويقدّم دالة بسيطة للتحقق من حالة أي ميزة.
أنشئ مجلداً جديداً باسم hooks/ وملفاً باسم useFeatureToggle.ts:
mkdir hooks
touch useFeatureToggle.ts
ثم أضف ما يلي:
// file: hooks/useFeatureToggle.ts
import React, { useContext } from "react";
import { FeatureToggleContext } from "../context/FeatureToggleContext";
export const useFeatureToggle = () => {
const { enabledFeatures } = useContext(FeatureToggleContext);
const isEnabled = (featureName: string) => {
return enabledFeatures.includes(featureName);
};
return [isEnabled];
};
هذه الدالة تُرجع مساعداً باسم isEnabled يمكنه التحقق مما إذا كانت ميزة معينة مفعلة أم لا.
استخدام Feature Toggle داخل الصفحة الرئيسية
الآن افتح الملف pages/index.tsx واستخدم الخطاف المخصص لعرض مخطط Treasury فقط إذا كانت الميزة مفعلة:
const [isEnabled] = useFeatureToggle();
return (
<>
{isEnabled("treasury_chart") && (<TreasuryChart />)}
</>
);
وهذا هو الشكل الكامل للصفحة:
// file: pages/index.tsx
import React from "react";
import Head from "next/head";
import { Layout } from "antd";
import { GdpChart, TreasuryChart } from "../components/Charts";
import { useFeatureToggle } from "../hooks/useFeatureToggle";
const { Header, Content } = Layout;
export default function Home() {
const [isEnabled] = useFeatureToggle();
return (
<Layout className="layout">
<Head>
<title>🚦 Feature Toggle in Next.js</title>
</Head>
<Header>
<div className="logo" />
</Header>
<Content className="content">
<GdpChart />
{isEnabled("treasury_chart") && (<TreasuryChart />)}
</Content>
</Layout>
);
}
عند هذه المرحلة سيعمل الإخفاء والإظهار بناءً على القائمة المرسلة يدوياً. لكن هذا ليس الحل المثالي، لأن أي تغيير سيتطلب تعديل الكود وإعادة نشره.
الانتقال إلى متغيرات البيئة Environment Variables
لكي يصبح النظام أكثر مرونة، من الأفضل قراءة حالة الميزات من متغيرات البيئة. بهذه الطريقة نستطيع تغيير حالة الميزة دون تعديل الشيفرة نفسها.
أنشئ ملف .env داخل المشروع:
touch .env
ثم أضف المتغير التالي:
# file: .env
FEATURE_TREASURY_CHART=false
تذكر أن قيم متغيرات البيئة تصل دائماً على هيئة نصوص strings، لذلك لا يمكن مقارنتها مباشرة بالقيم المنطقية true وfalse إلا كنصوص.
إنشاء واجهة API داخل Next.js لقراءة الميزات
الآن نحتاج إلى نقطة وصول API تعيد قائمة الميزات المفعلة بناءً على القيم الموجودة في process.env. أنشئ ملفاً باسم pages/api/features.ts:
// file: pages/api/features.ts
export default (req, res) => {
res.status(200).json([
process.env.FEATURE_TREASURY_CHART === "true" ? "treasury_chart" : "",
]);
};
بعد ذلك جرّب فتح الرابط http://localhost:3000/api/features في المتصفح. إذا كانت الميزة معطلة فستحصل على ناتج مشابه:
[""]
أما إذا جعلت القيمة FEATURE_TREASURY_CHART=true ثم أعدت تشغيل الخادم، فستجد أن القائمة تتضمن treasury_chart.
ربط التطبيق بواجهة الميزات باستخدام axios
سنحتاج الآن إلى إجراء طلب HTTP من التطبيق إلى الواجهة التي أنشأناها. يمكن تنفيذ ذلك باستخدام fetch، لكن مكتبة axios تمنحك أسلوباً أكثر تنظيماً في التعامل مع الطلبات والأخطاء.
ثبت المكتبة أولاً:
$ npm install --save axios
ثم أنشئ ملف خدمة داخل مجلد services/ باسم FeatureToggle.ts:
// File: services/FeatureToggle.ts
import axios from "axios";
export const fetchFeatures = async () => {
try {
const { data } = await axios.get<string[]>("/api/features");
return data;
} catch (e) {
console.log("Something went wrong");
}
return [] as string[];
};
تحميل الميزات عند بدء التطبيق
بعد تجهيز خدمة القراءة، عد إلى الملف pages/_app.tsx واستخدم الخطافين useState وuseEffect لتحميل الميزات عند بدء التطبيق:
const [enabledFeatures, setFeatures] = useState<string[]>([]);
const processFeatures = async () => {
const features = await fetchFeatures();
setFeatures(features);
};
useEffect(() => {
processFeatures();
}, []);
وهذا هو الشكل النهائي للملف:
// file: pages/_app.tsx
import { useEffect, useState } from "react";
import { FeatureToggle } from "../components/FeatureToggleProvider";
import { fetchFeatures } from "../services/FeatureToggle";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
const [enabledFeatures, setFeatures] = useState<string[]>([]);
const processFeatures = async () => {
const features = await fetchFeatures();
setFeatures(features);
};
useEffect(() => {
processFeatures();
}, []);
return (
<FeatureToggle enabledFeatures={enabledFeatures}>
<Component {...pageProps} />
</FeatureToggle>
);
}
export default MyApp;
الآن أصبح نظام Feature Toggle يعمل بشكل ديناميكي اعتماداً على متغيرات البيئة، بدلاً من القائمة الثابتة داخل الكود.
نشر التطبيق على Vercel
منصة Vercel خيار ممتاز لتطبيقات Next.js لأنها توفر استضافة سهلة وتدعم متغيرات البيئة بشكل مدمج. بعد إنشاء حساب وتسجيل الدخول، ابدأ مشروعاً جديداً من لوحة التحكم.

اختر المستودع المناسب من GitHub:

بعدها افتح قسم Environment Variables وأضف المتغير FEATURE_TREASURY_CHART بالقيمة false ثم انقر على زر النشر:

بعد اكتمال النشر ستظهر شاشة النجاح:

يمكنك زيارة التطبيق من خلال لوحة التحكم:

في هذه الحالة سيظهر مخطط واحد فقط لأن الميزة الثانية معطلة:

كيفية تفعيل الميزة لاحقاً دون تعديل الكود
إذا أردت إظهار مخطط Treasury لاحقاً، افتح إعدادات المشروع في Vercel ثم انتقل إلى قسم Environment Variables وغيّر قيمة FEATURE_TREASURY_CHART إلى true:

بعد ذلك تحتاج إلى إعادة النشر حتى يلتقط التطبيق القيم الجديدة:

وعند اكتمال النشر سيظهر المخطط الثاني على الموقع:

أفضل الممارسات عند استخدام Feature Toggles
1) لا تُبقِ المفاتيح المهجورة داخل المشروع
عندما تنتهي من اختبار ميزة وأصبحت مستقرة في الإنتاج، يُفضّل إزالة المفتاح المرتبط بها من الكود. تراكم المفاتيح القديمة يجعل المشروع أكثر تعقيداً وصعوبة في الصيانة.
2) استخدم أسماء واضحة وقابلة للفهم
من الأفضل أن تكون أسماء الميزات مثل treasury_chart أو new_checkout_flow بدلاً من أسماء غامضة لا تعبّر عن الغرض الحقيقي منها.
3) ميّز بين ميزات الواجهة والمنطق الحساس
يمكن استخدام Feature Toggle بسهولة في عرض المكونات، لكن عند التعامل مع منطق حساس أو صلاحيات أو عمليات مالية، يجب أن يكون التحكم من جهة الخادم أيضاً وليس من الواجهة فقط.
4) راقب الأثر على الأداء
في المشاريع الكبيرة، كثرة المفاتيح والاستعلامات قد تزيد التعقيد. لذلك من الجيد تجميع الإعدادات، وتخزينها بكفاءة، أو تحميلها مرة واحدة عند بدء التطبيق.
فوائد هذا النهج من منظور تقني وعملي
- تقليل المخاطر أثناء النشر.
- إمكانية اختبار الميزات بشكل تدريجي.
- فصل إدارة التشغيل عن الشيفرة البرمجية.
- مرونة أعلى لفرق التطوير والمنتج.
- تجربة أفضل في التعامل مع البيئات المختلفة مثل
developmentوstagingوproduction.
الخلاصة التقنية
يُعد بناء نظام Feature Toggle داخل Next.js باستخدام React Context API وCustom Hooks وEnvironment Variables خطوة ذكية لأي فريق يريد تطوير الميزات ونشرها بأمان ومرونة. هذه البنية ليست معقدة، لكنها تمنحك أساساً متيناً للتوسع لاحقاً، سواء بإضافة لوحة تحكم داخلية أو ربط النظام بخدمة خارجية متخصصة في إدارة Feature Flags. تقنياً، هذا الأسلوب مناسب جداً للمشاريع المتوسطة والبدايات الاحترافية، بشرط الحفاظ على بساطة المفاتيح وتنظيفها باستمرار مع تطور المنتج.