diff --git a/apps/vr-tests-react-components/screener.config.js b/apps/vr-tests-react-components/screener.config.js
index 56a9934f99dc1c..0e229136d99d10 100644
--- a/apps/vr-tests-react-components/screener.config.js
+++ b/apps/vr-tests-react-components/screener.config.js
@@ -24,7 +24,7 @@ function getCurrentHash() {
  * @param {string} options.sourceBranchName
  * @param {string} options.deployUrl
  * @param {string} options.targetBranch
- * @returns
+ * @returns {import('@fluentui/scripts/screener/screener.types').ScreenerRunnerConfig}
  */
 function getConfig({ screenerApiKey, sourceBranchName, deployUrl, targetBranch }) {
   const baseBranch = targetBranch ? targetBranch.replace(/^refs\/heads\//, '') : 'master';
@@ -38,6 +38,7 @@ function getConfig({ screenerApiKey, sourceBranchName, deployUrl, targetBranch }
     baseBranch,
     failureExitCode: 0,
     alwaysAcceptBaseBranch: true,
+    states: [],
     ...(sourceBranchName !== 'master' ? { commit: getCurrentHash() } : null),
     baseUrl: `${deployUrl}/react-components-screener/iframe.html`,
   };
diff --git a/apps/vr-tests/screener.config.js b/apps/vr-tests/screener.config.js
index b928986b51524a..13f073e99c97d0 100644
--- a/apps/vr-tests/screener.config.js
+++ b/apps/vr-tests/screener.config.js
@@ -27,7 +27,7 @@ function getCurrentHash() {
  * @param {string} options.sourceBranchName
  * @param {string} options.deployUrl
  * @param {string} options.targetBranch
- * @returns
+ * @returns {import('@fluentui/scripts/screener/screener.types').ScreenerRunnerConfig}
  */
 function getConfig({ screenerApiKey, sourceBranchName, deployUrl, targetBranch }) {
   const baseBranch = targetBranch ? targetBranch.replace(/^refs\/heads\//, '') : 'master';
@@ -44,6 +44,7 @@ function getConfig({ screenerApiKey, sourceBranchName, deployUrl, targetBranch }
     alwaysAcceptBaseBranch: true,
     ...(sourceBranchName !== 'master' ? { commit: getCurrentHash() } : null),
     baseUrl: `${deployUrl}/react-screener/iframe.html`,
+    states: [],
   };
   console.log('Screener config: ' + JSON.stringify({ ...config, apiKey: '...' }, null, 2));
   return config;
diff --git a/gulpfile.ts b/gulpfile.ts
index e7f72e8db3565c..dd562b2ee3f980 100644
--- a/gulpfile.ts
+++ b/gulpfile.ts
@@ -22,7 +22,8 @@ tsPaths.register({
 require('./scripts/gulp/tasks/bundle');
 require('./scripts/gulp/tasks/component-info');
 require('./scripts/gulp/tasks/docs');
-require('./scripts/gulp/tasks/screener');
+require('./scripts/gulp/tasks/vr-build');
+require('./scripts/gulp/tasks/vr-test');
 require('./scripts/gulp/tasks/stats');
 require('./scripts/gulp/tasks/test-unit');
 require('./scripts/gulp/tasks/perf');
diff --git a/scripts/gulp/tasks/vr-build.ts b/scripts/gulp/tasks/vr-build.ts
new file mode 100644
index 00000000000000..6fe6cb4460b0ff
--- /dev/null
+++ b/scripts/gulp/tasks/vr-build.ts
@@ -0,0 +1,21 @@
+import { task, series } from 'gulp';
+import fs from 'fs';
+
+import config from '../../config';
+import getScreenerStates from '../../screener/screener.states';
+
+const { paths } = config;
+
+task('screener:states', cb => {
+  const states = getScreenerStates();
+  const statesJson = JSON.stringify(states, null, 2);
+  fs.writeFile(paths.docsDist('screenerStates.json'), statesJson, { encoding: 'utf8' }, err => {
+    if (err) {
+      cb(err);
+    }
+
+    cb();
+  });
+});
+
+task('screener:build', series('build:docs', 'screener:states'));
diff --git a/scripts/gulp/tasks/screener.ts b/scripts/gulp/tasks/vr-test.ts
similarity index 76%
rename from scripts/gulp/tasks/screener.ts
rename to scripts/gulp/tasks/vr-test.ts
index 329bf716c30e82..9e99e85fda6182 100644
--- a/scripts/gulp/tasks/screener.ts
+++ b/scripts/gulp/tasks/vr-test.ts
@@ -1,11 +1,13 @@
-import { task, series } from 'gulp';
+import { task } from 'gulp';
 import { argv } from 'yargs';
 
 import config from '../../config';
 import { getAllPackageInfo } from '../../monorepo';
 import { screenerRunner } from '../../screener/screener.runner';
+import getConfig from '../../screener/screener.config';
 
 const { paths } = config;
+const docsPackageName = '@fluentui/docs';
 
 // ----------------------------------------
 // Visual
@@ -15,15 +17,11 @@ task('screener:runner', cb => {
   // screener-runner doesn't allow to pass custom options
   if (argv.filter) process.env.SCREENER_FILTER = argv.filter as string;
 
-  const docsPackageName = '@fluentui/docs';
-
   const packageInfos = getAllPackageInfo();
   if (Object.values(packageInfos).every(packageInfo => packageInfo.packageJson.name !== docsPackageName)) {
     throw new Error(`package ${docsPackageName} does not exist in the repo`);
   }
 
-  const screenerConfigPath = paths.base('scripts/screener/screener.config.js');
-
   // kill the server when done
   const handlePromiseExit = promise =>
     promise
@@ -36,17 +34,14 @@ task('screener:runner', cb => {
         process.exit(1);
       });
 
-  const getConfig = require(screenerConfigPath);
   const screenerConfig = getConfig({
     screenerApiKey: process.env.SCREENER_API_KEY,
     sourceBranchName: process.env.BUILD_SOURCEBRANCHNAME,
+    deployUrl: process.env.DEPLOYURL,
   });
 
+  const screenerStates = require(paths.docsDist('screenerStates.json'));
+  screenerConfig.states = screenerStates;
+
   handlePromiseExit(screenerRunner(screenerConfig));
 });
-
-// ----------------------------------------
-// Default
-// ----------------------------------------
-
-task('screener:build', series('build:docs'));
diff --git a/scripts/screener/screener.config.js b/scripts/screener/screener.config.js
index 64384043d2cab5..ee63d4dea9ec77 100644
--- a/scripts/screener/screener.config.js
+++ b/scripts/screener/screener.config.js
@@ -36,11 +36,13 @@ const baseBranch = 'master';
  * @param {Object} options
  * @param {string} options.screenerApiKey
  * @param {string} options.sourceBranchName
- * @returns
+ * @param {string} options.deployUrl
+ * @returns {import('./screener.types').ScreenerRunnerConfig}
  */
-function getConfig({ screenerApiKey, sourceBranchName }) {
+function getConfig({ screenerApiKey, sourceBranchName, deployUrl }) {
   // https://github.com/screener-io/screener-runner
   return {
+    baseUrl: `${deployUrl}/react-northstar-screener`,
     apiKey: screenerApiKey,
     projectRepo: 'microsoft/fluentui/fluentui',
 
@@ -54,9 +56,7 @@ function getConfig({ screenerApiKey, sourceBranchName }) {
       minShiftGraphic: 1, // Optional threshold for pixel shifts in graphics.
       compareSVGDOM: false, // Pass if SVG DOM is the same. Defaults to false.
     },
-
-    // screenshot every example in maximized mode
-    states: require('./screener.states').default,
+    states: [],
 
     alwaysAcceptBaseBranch: true,
     baseBranch,
diff --git a/scripts/screener/screener.states.ts b/scripts/screener/screener.states.ts
index 20ad7881f1b9fc..5876517c02c5df 100644
--- a/scripts/screener/screener.states.ts
+++ b/scripts/screener/screener.states.ts
@@ -7,42 +7,44 @@ import path from 'path';
 import getScreenerSteps from './screener.steps';
 import { ScreenerState } from './screener.types';
 
-const baseUrl = `${process.env.DEPLOYURL}/react-northstar-screener`;
-const examplePaths = glob.sync('packages/fluentui/docs/src/examples/**/*.tsx', {
-  ignore: ['**/index.tsx', '**/*.knobs.tsx', '**/BestPractices/*.tsx', '**/Playground.tsx'],
-});
-
-const pathFilter = process.env.SCREENER_FILTER;
-const filteredPaths: string[] = minimatch.match(examplePaths, pathFilter || '*', {
-  matchBase: true,
-});
-
-if (pathFilter) {
-  console.log(chalk.bgGreen.black(' --filter '), pathFilter);
-  filteredPaths.forEach(filteredPath => console.log(`${_.repeat(' ', 10)} ${filteredPath}`));
-}
-
-const getStateForPath = (examplePath: string): ScreenerState => {
-  const { name: exampleNameWithoutExtension, base: exampleNameWithExtension, dir: exampleDir } = path.parse(
-    examplePath,
-  );
-
-  const rtl = exampleNameWithExtension.endsWith('.rtl.tsx');
-  const exampleUrl = _.kebabCase(exampleNameWithoutExtension);
-  const pageUrl = `${baseUrl}/maximize/${exampleUrl}/${rtl}`;
-
-  return {
-    url: pageUrl,
-    name: exampleNameWithExtension,
-
-    // https://www.npmjs.com/package/screener-runner#testing-interactions
-    steps: getScreenerSteps(pageUrl, `${exampleDir}/${exampleNameWithoutExtension}.steps`),
+export default function getScreenerStates() {
+  const baseUrl = `${process.env.DEPLOYURL}/react-northstar-screener`;
+  const examplePaths = glob.sync('packages/fluentui/docs/src/examples/**/*.tsx', {
+    ignore: ['**/index.tsx', '**/*.knobs.tsx', '**/BestPractices/*.tsx', '**/Playground.tsx'],
+  });
+
+  const pathFilter = process.env.SCREENER_FILTER;
+  const filteredPaths: string[] = minimatch.match(examplePaths, pathFilter || '*', {
+    matchBase: true,
+  });
+
+  if (pathFilter) {
+    console.log(chalk.bgGreen.black(' --filter '), pathFilter);
+    filteredPaths.forEach(filteredPath => console.log(`${_.repeat(' ', 10)} ${filteredPath}`));
+  }
+
+  const getStateForPath = (examplePath: string): ScreenerState => {
+    const { name: exampleNameWithoutExtension, base: exampleNameWithExtension, dir: exampleDir } = path.parse(
+      examplePath,
+    );
+
+    const rtl = exampleNameWithExtension.endsWith('.rtl.tsx');
+    const exampleUrl = _.kebabCase(exampleNameWithoutExtension);
+    const pageUrl = `${baseUrl}/maximize/${exampleUrl}/${rtl}`;
+
+    return {
+      url: pageUrl,
+      name: exampleNameWithExtension,
+
+      // https://www.npmjs.com/package/screener-runner#testing-interactions
+      steps: getScreenerSteps(pageUrl, `${exampleDir}/${exampleNameWithoutExtension}.steps`),
+    };
   };
-};
 
-const screenerStates = filteredPaths.reduce((states, examplePath) => {
-  states.push(getStateForPath(examplePath));
-  return states;
-}, [] as ReturnType<typeof getStateForPath>[]);
+  const screenerStates = filteredPaths.reduce((states, examplePath) => {
+    states.push(getStateForPath(examplePath));
+    return states;
+  }, [] as ReturnType<typeof getStateForPath>[]);
 
-export default screenerStates;
+  return screenerStates;
+}
diff --git a/scripts/screener/screener.types.ts b/scripts/screener/screener.types.ts
index 087f92d7ef536d..d7f38c24ec7f43 100644
--- a/scripts/screener/screener.types.ts
+++ b/scripts/screener/screener.types.ts
@@ -2,7 +2,7 @@ export type ScreenerRunnerConfig = {
   apiKey: string;
   projectRepo: string;
 
-  diffOptions: {
+  diffOptions?: {
     structure: boolean;
     layout: boolean;
     style: boolean;
@@ -10,14 +10,14 @@ export type ScreenerRunnerConfig = {
     minLayoutPosition: number; // Optional threshold for Layout changes. Defaults to 4 pixels.
     minLayoutDimension: number; // Optional threshold for Layout changes. Defaults to 10 pixels.
     minShiftGraphic: number; // Optional threshold for pixel shifts in graphics.
-    compareSVGDOM: number; // Pass if SVG DOM is the same. Defaults to false.
+    compareSVGDOM: boolean; // Pass if SVG DOM is the same. Defaults to false.
   };
 
   states: ScreenerState[];
 
   alwaysAcceptBaseBranch: boolean;
   baseBranch: string;
-  commit: string;
+  commit?: string;
   failureExitCode: number;
 
   /** Base url of deployed storybook screener should test */