diff --git a/enterprise/frontend/src/metabase-enterprise/sharing/components/MutableParametersSection.jsx b/enterprise/frontend/src/metabase-enterprise/sharing/components/MutableParametersSection.jsx
index bb37b3fc8ffb2cc616c24c5a4841d86865a9bd54..ceb9e8b40b907c1f861df2aa33156b711f38538e 100644
--- a/enterprise/frontend/src/metabase-enterprise/sharing/components/MutableParametersSection.jsx
+++ b/enterprise/frontend/src/metabase-enterprise/sharing/components/MutableParametersSection.jsx
@@ -10,7 +10,7 @@ import { t } from "ttag";
 import CollapseSection from "metabase/components/CollapseSection";
 import ParametersList from "metabase/parameters/components/ParametersList";
 
-import { collateParametersWithValues } from "metabase/meta/Parameter";
+import { getValuePopulatedParameters } from "metabase/meta/Parameter";
 import {
   getPulseParameters,
   getActivePulseParameters,
@@ -31,7 +31,7 @@ function MutableParametersSection({
     return map;
   }, {});
 
-  const collatedParameters = collateParametersWithValues(
+  const valuePopulatedParameters = getValuePopulatedParameters(
     parameters,
     pulseParamValuesById,
   );
@@ -63,7 +63,7 @@ function MutableParametersSection({
         className="align-stretch row-gap-1"
         vertical
         dashboard={dashboard}
-        parameters={collatedParameters}
+        parameters={valuePopulatedParameters}
         setParameterValue={setParameterValue}
       />
     </CollapseSection>
diff --git a/frontend/src/metabase-lib/lib/Question.js b/frontend/src/metabase-lib/lib/Question.js
index aa7146ee499f30e76b9124f33d7acee6ba9304e1..787ff9c06c1875654c2045032990670f6a0ae793 100644
--- a/frontend/src/metabase-lib/lib/Question.js
+++ b/frontend/src/metabase-lib/lib/Question.js
@@ -35,7 +35,10 @@ import {
   findColumnIndexForColumnSetting,
   syncTableColumnsToQuery,
 } from "metabase/lib/dataset";
-import { getParametersWithExtras, isTransientId } from "metabase/meta/Card";
+import {
+  getValueAndFieldIdPopulatedParametersFromCard,
+  isTransientId,
+} from "metabase/meta/Card";
 import {
   parameterToMBQLFilter,
   normalizeParameterValue,
@@ -969,7 +972,10 @@ export default class Question {
 
   // TODO: Fix incorrect Flow signature
   parameters(): ParameterObject[] {
-    return getParametersWithExtras(this.card(), this._parameterValues);
+    return getValueAndFieldIdPopulatedParametersFromCard(
+      this.card(),
+      this._parameterValues,
+    );
   }
 
   parametersList(): ParameterObject[] {
diff --git a/frontend/src/metabase/dashboard/components/DashCard.jsx b/frontend/src/metabase/dashboard/components/DashCard.jsx
index 776e944400f19b77323c2b76c9c1770e04c3cb49..7edadc1e9b49b8f4e7e6d70508f1d91b7c0de09e 100644
--- a/frontend/src/metabase/dashboard/components/DashCard.jsx
+++ b/frontend/src/metabase/dashboard/components/DashCard.jsx
@@ -29,7 +29,7 @@ import { getClickBehaviorDescription } from "metabase/lib/click-behavior";
 import cx from "classnames";
 import _ from "underscore";
 import { getIn } from "icepick";
-import { getParametersBySlug } from "metabase/meta/Parameter";
+import { getParameterValuesBySlug } from "metabase/meta/Parameter";
 import Utils from "metabase/lib/utils";
 
 const DATASET_USUALLY_FAST_THRESHOLD = 15 * 1000;
@@ -159,7 +159,10 @@ export default class DashCard extends Component {
       errorIcon = "warning";
     }
 
-    const params = getParametersBySlug(dashboard.parameters, parameterValues);
+    const parameterValuesBySlug = getParameterValuesBySlug(
+      dashboard.parameters,
+      parameterValues,
+    );
 
     const hideBackground =
       !isEditing &&
@@ -217,7 +220,7 @@ export default class DashCard extends Component {
           isDashboard
           dispatch={this.props.dispatch}
           dashboard={dashboard}
-          parameterValuesBySlug={params}
+          parameterValuesBySlug={parameterValuesBySlug}
           isEditing={isEditing}
           isPreviewing={this.state.isPreviewingCard}
           gridSize={
@@ -231,7 +234,7 @@ export default class DashCard extends Component {
                 className="m1 text-brand-hover text-light"
                 classNameClose="hover-child"
                 card={dashcard.card}
-                params={params}
+                params={parameterValuesBySlug}
                 dashcardId={dashcard.id}
                 token={dashcard.dashboard_id}
                 icon="download"
diff --git a/frontend/src/metabase/dashboard/components/Dashboard/ParametersWidget/ParametersWidget.jsx b/frontend/src/metabase/dashboard/components/Dashboard/ParametersWidget/ParametersWidget.jsx
index 064d3581ed1ceeacc57fe8972f139dd29fbff938..f20260f16322a068953260c0d45ae49031af2704 100644
--- a/frontend/src/metabase/dashboard/components/Dashboard/ParametersWidget/ParametersWidget.jsx
+++ b/frontend/src/metabase/dashboard/components/Dashboard/ParametersWidget/ParametersWidget.jsx
@@ -1,6 +1,7 @@
 import React from "react";
 import PropTypes from "prop-types";
 
+import { getValuePopulatedParameters } from "metabase/meta/Parameter";
 import Parameters from "metabase/parameters/components/Parameters/Parameters";
 
 const propTypes = {
@@ -46,10 +47,7 @@ const ParametersWidget = ({
       isFullscreen={isFullscreen}
       isNightMode={shouldRenderAsNightMode}
       hideParameters={hideParameters}
-      parameters={parameters.map(p => ({
-        ...p,
-        value: parameterValues[p.id],
-      }))}
+      parameters={getValuePopulatedParameters(parameters, parameterValues)}
       query={location.query}
       editingParameter={editingParameter}
       setEditingParameter={setEditingParameter}
diff --git a/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx b/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx
index 3e280c2068f93e8079f9f58a4dc1973c533e011f..86ac8bec089ad327d048a00debeedabec85fbc86 100644
--- a/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx
+++ b/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx
@@ -8,6 +8,7 @@ import cx from "classnames";
 import title from "metabase/hoc/Title";
 import withToast from "metabase/hoc/Toast";
 import DashboardData from "metabase/dashboard/hoc/DashboardData";
+import { getValuePopulatedParameters } from "metabase/meta/Parameter";
 
 import ActionButton from "metabase/components/ActionButton";
 import Button from "metabase/components/Button";
@@ -144,10 +145,10 @@ class AutomaticDashboardApp extends React.Component {
             {parameters && parameters.length > 0 && (
               <div className="px1 pt1">
                 <Parameters
-                  parameters={parameters.map(p => ({
-                    ...p,
-                    value: parameterValues && parameterValues[p.id],
-                  }))}
+                  parameters={getValuePopulatedParameters(
+                    parameters,
+                    parameterValues,
+                  )}
                   query={location.query}
                   setParameterValue={setParameterValue}
                   syncQueryString
diff --git a/frontend/src/metabase/dashboard/dashboard.js b/frontend/src/metabase/dashboard/dashboard.js
index 5a0a0198afda280b9e06be26a5173fca391492d6..9ce2b60270b3ba579d98d451b2d9962aa6c16c32 100644
--- a/frontend/src/metabase/dashboard/dashboard.js
+++ b/frontend/src/metabase/dashboard/dashboard.js
@@ -31,7 +31,10 @@ import {
   setParameterDefaultValue as setParamDefaultValue,
 } from "metabase/meta/Dashboard";
 import { applyParameters, questionUrlWithParameters } from "metabase/meta/Card";
-import { getParametersBySlug } from "metabase/meta/Parameter";
+import {
+  getParameterValuesBySlug,
+  getParameterValuesByIdFromQueryParams,
+} from "metabase/meta/Parameter";
 import * as Urls from "metabase/lib/urls";
 
 import type {
@@ -378,7 +381,7 @@ export const saveDashboardAndCards = createThunkAction(
               parameter_mappings &&
               parameter_mappings.filter(
                 mapping =>
-                  // filter out mappings for deleted paramters
+                  // filter out mappings for deleted parameters
                   _.findWhere(dashboard.parameters, {
                     id: mapping.parameter_id,
                   }) &&
@@ -593,7 +596,7 @@ export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(
             token: dashcard.dashboard_id,
             dashcardId: dashcard.id,
             cardId: card.id,
-            ...getParametersBySlug(dashboard.parameters, parameterValues),
+            ...getParameterValuesBySlug(dashboard.parameters, parameterValues),
             ignore_cache: ignoreCache,
           },
           queryOptions,
@@ -663,7 +666,6 @@ function expandInlineCard(card) {
 export const fetchDashboard = createThunkAction(FETCH_DASHBOARD, function(
   dashId,
   queryParams,
-  enableDefaultParameters = true,
 ) {
   let result;
   return async function(dispatch, getState) {
@@ -712,17 +714,6 @@ export const fetchDashboard = createThunkAction(FETCH_DASHBOARD, function(
       result = await DashboardApi.get({ dashId: dashId });
     }
 
-    const parameterValues = {};
-    if (result.parameters) {
-      for (const parameter of result.parameters) {
-        if (queryParams && queryParams[parameter.slug] != null) {
-          parameterValues[parameter.id] = queryParams[parameter.slug];
-        } else if (enableDefaultParameters && parameter.default != null) {
-          parameterValues[parameter.id] = parameter.default;
-        }
-      }
-    }
-
     if (dashboardType === "normal" || dashboardType === "transient") {
       dispatch(loadMetadataForDashboard(result.ordered_cards));
     }
@@ -744,10 +735,15 @@ export const fetchDashboard = createThunkAction(FETCH_DASHBOARD, function(
       dispatch(addFields(result.param_fields));
     }
 
+    const parameterValuesById = getParameterValuesByIdFromQueryParams(
+      result.parameters,
+      queryParams,
+    );
+
     return {
       ...normalize(result, dashboard), // includes `result` and `entities`
       dashboardId: dashId,
-      parameterValues: parameterValues,
+      parameterValues: parameterValuesById,
     };
   };
 });
@@ -920,9 +916,12 @@ export const setParameterValue = createThunkAction(
   },
 );
 
-export const setOrUnsetParameterValues = pairs => (dispatch, getState) => {
+export const setOrUnsetParameterValues = parameterIdValuePairs => (
+  dispatch,
+  getState,
+) => {
   const parameterValues = getParameterValues(getState());
-  pairs
+  parameterIdValuePairs
     .map(([id, value]) =>
       setParameterValue(id, value === parameterValues[id] ? null : value),
     )
diff --git a/frontend/src/metabase/lib/pulse.js b/frontend/src/metabase/lib/pulse.js
index bcbac0c7d19bb451ff9fa869213d5badb0aefbe2..771302ced34beff4176f88e707e082124f8bde87 100644
--- a/frontend/src/metabase/lib/pulse.js
+++ b/frontend/src/metabase/lib/pulse.js
@@ -136,7 +136,7 @@ export function getActivePulseParameters(pulse, parameters) {
 
       return {
         ...parameter,
-        value: hasParameterValue(pulseParameter)
+        value: hasParameterValue(pulseParameter?.value)
           ? pulseParameter.value
           : parameter.default,
       };
diff --git a/frontend/src/metabase/meta/Card.js b/frontend/src/metabase/meta/Card.js
index be6641bcd2f5b0f53961c26e7488b0144b74ad8f..0a51386ff8952d5ab602ae5c4b5fbf73acb7e06e 100644
--- a/frontend/src/metabase/meta/Card.js
+++ b/frontend/src/metabase/meta/Card.js
@@ -3,6 +3,7 @@ import {
   getParameterTargetFieldId,
   parameterToMBQLFilter,
   normalizeParameterValue,
+  getValuePopulatedParameters,
 } from "metabase/meta/Parameter";
 
 import * as Query from "metabase/lib/query/query";
@@ -125,7 +126,7 @@ export function getTemplateTagsForParameters(card: ?Card): Array<TemplateTag> {
   );
 }
 
-export function getParameters(card: ?Card): Parameter[] {
+export function getParametersFromCard(card: ?Card): Parameter[] {
   if (card && card.parameters) {
     return card.parameters;
   }
@@ -134,15 +135,17 @@ export function getParameters(card: ?Card): Parameter[] {
   return getTemplateTagParameters(tags);
 }
 
-export function getParametersWithExtras(
+export function getValueAndFieldIdPopulatedParametersFromCard(
   card: Card,
   parameterValues?: ParameterValues,
 ): Parameter[] {
-  return getParameters(card).map(parameter => {
-    // if we have a parameter value for this parameter, set "value"
-    if (parameterValues && parameter.id in parameterValues) {
-      parameter = assoc(parameter, "value", parameterValues[parameter.id]);
-    }
+  const parameters = getParametersFromCard(card);
+  const valuePopulatedParameters = getValuePopulatedParameters(
+    parameters,
+    parameterValues,
+  );
+
+  return valuePopulatedParameters.map(parameter => {
     // if we have a field id for this parameter, set "field_id"
     const fieldId = getParameterTargetFieldId(
       parameter.target,
@@ -232,7 +235,7 @@ export function questionUrlWithParameters(
 
   card = Utils.copy(card);
 
-  const cardParameters = getParameters(card);
+  const cardParameters = getParametersFromCard(card);
   const datasetQuery = applyParameters(
     card,
     parameters,
diff --git a/frontend/src/metabase/meta/Parameter.js b/frontend/src/metabase/meta/Parameter.js
index 545b0e0ade374021e5beca8698b1b3aad239002b..226c28593f99e951bdaf229a0e323bfddceb3702 100644
--- a/frontend/src/metabase/meta/Parameter.js
+++ b/frontend/src/metabase/meta/Parameter.js
@@ -13,7 +13,6 @@ import type {
   ParameterTarget,
   ParameterValue,
   ParameterValueOrArray,
-  ParameterValues,
 } from "metabase-types/types/Parameter";
 import type { FieldId } from "metabase-types/types/Field";
 import type Metadata from "metabase-lib/lib/metadata/Metadata";
@@ -371,19 +370,6 @@ export function getTemplateTagParameters(tags: TemplateTag[]): Parameter[] {
     });
 }
 
-export const getParametersBySlug = (
-  parameters: Parameter[],
-  parameterValues: ParameterValues,
-): { [key: string]: string } => {
-  const result = {};
-  for (const parameter of parameters) {
-    if (parameterValues[parameter.id] != null) {
-      result[parameter.slug] = parameterValues[parameter.id];
-    }
-  }
-  return result;
-};
-
 /** Returns the field ID that this parameter target points to, or null if it's not a dimension target. */
 export function getParameterTargetFieldId(
   target: ?ParameterTarget,
@@ -619,21 +605,75 @@ function splitType(parameterOrType) {
   return parameterType.split("/");
 }
 
-export function collateParametersWithValues(parameters, parameterValues) {
-  if (parameterValues) {
-    return parameters.map(p => ({
-      ...p,
-      value: parameterValues[p.id],
-    }));
-  } else {
-    return parameters;
-  }
+export function getValuePopulatedParameters(parameters, parameterValues) {
+  return parameterValues
+    ? parameters.map(parameter => {
+        return parameter.id in parameterValues
+          ? {
+              ...parameter,
+              value: parameterValues[parameter.id],
+            }
+          : parameter;
+      })
+    : parameters;
 }
 
 export function hasDefaultParameterValue(parameter) {
   return parameter.default != null;
 }
 
-export function hasParameterValue(parameter) {
-  return parameter && parameter.value != null;
+export function hasParameterValue(value) {
+  return value != null;
+}
+
+export function getParameterValueFromQueryParams(parameter, queryParams) {
+  queryParams = queryParams || {};
+  const maybeParameterValue = queryParams[parameter.slug];
+  return hasParameterValue(maybeParameterValue)
+    ? maybeParameterValue
+    : parameter.default;
+}
+
+export function getParameterValuePairsFromQueryParams(parameters, queryParams) {
+  return parameters
+    .map(parameter => [
+      parameter,
+      getParameterValueFromQueryParams(parameter, queryParams),
+    ])
+    .filter(([, value]) => hasParameterValue(value));
+}
+
+export function getParameterValuesByIdFromQueryParams(parameters, queryParams) {
+  const idValuePairs = getParameterValuePairsFromQueryParams(
+    parameters,
+    queryParams,
+  ).map(([parameter, value]) => [parameter.id, value]);
+
+  return Object.fromEntries(idValuePairs);
+}
+
+export function getParameterValuesBySlug(parameters, parameterValuesById) {
+  parameterValuesById = parameterValuesById || {};
+  const slugValuePairs = parameters
+    .map(parameter => [
+      parameter.slug,
+      parameter.value || parameterValuesById[parameter.id],
+    ])
+    .filter(([, value]) => hasParameterValue(value));
+
+  return Object.fromEntries(slugValuePairs);
+}
+
+export function buildHiddenParametersSlugSet(hiddenParameterSlugs) {
+  return _.isString(hiddenParameterSlugs)
+    ? new Set(hiddenParameterSlugs.split(","))
+    : new Set();
+}
+
+export function getVisibleParameters(parameters, hiddenParameterSlugs) {
+  const hiddenParametersSlugSet = buildHiddenParametersSlugSet(
+    hiddenParameterSlugs,
+  );
+
+  return parameters.filter(p => !hiddenParametersSlugSet.has(p.slug));
 }
diff --git a/frontend/src/metabase/parameters/components/Parameters/Parameters.jsx b/frontend/src/metabase/parameters/components/Parameters/Parameters.jsx
index 163f605283b6a76b234d17ba5c84fdfd569f78ee..8dec133915d5d01f35f07d23c0283be82f28b0f7 100644
--- a/frontend/src/metabase/parameters/components/Parameters/Parameters.jsx
+++ b/frontend/src/metabase/parameters/components/Parameters/Parameters.jsx
@@ -5,7 +5,7 @@ import querystring from "querystring";
 
 import ParametersList from "metabase/parameters/components/ParametersList";
 import { syncQueryParamsWithURL } from "./syncQueryParamsWithURL";
-import { collateParametersWithValues } from "metabase/meta/Parameter";
+import { getParameterValuesBySlug } from "metabase/meta/Parameter";
 import { getMetadata } from "metabase/selectors/metadata";
 
 @connect(state => ({ metadata: getMetadata(state) }))
@@ -25,17 +25,12 @@ export default class Parameters extends Component {
 
     if (this.props.syncQueryString) {
       // sync parameters to URL query string
-      const queryParams = {};
-      for (const parameter of collateParametersWithValues(
+      const parameterValuesBySlug = getParameterValuesBySlug(
         parameters,
         parameterValues,
-      )) {
-        if (parameter.value) {
-          queryParams[parameter.slug] = parameter.value;
-        }
-      }
+      );
 
-      let search = querystring.stringify(queryParams);
+      let search = querystring.stringify(parameterValuesBySlug);
       search = search ? "?" + search : "";
 
       if (search !== window.location.search) {
diff --git a/frontend/src/metabase/parameters/components/Parameters/syncQueryParamsWithURL.js b/frontend/src/metabase/parameters/components/Parameters/syncQueryParamsWithURL.js
index ad5a3dd33dbaa973331ed18866ba86935e11dd7d..7aaf2668059029d693f0a299aa9cd9d91476c284 100644
--- a/frontend/src/metabase/parameters/components/Parameters/syncQueryParamsWithURL.js
+++ b/frontend/src/metabase/parameters/components/Parameters/syncQueryParamsWithURL.js
@@ -1,4 +1,9 @@
 import Dimension from "metabase-lib/lib/Dimension";
+import {
+  getParameterValueFromQueryParams,
+  getParameterValuePairsFromQueryParams,
+  hasParameterValue,
+} from "metabase/meta/Parameter";
 
 export const syncQueryParamsWithURL = props => {
   props.commitImmediately
@@ -13,15 +18,18 @@ const syncForInternalQuestion = props => {
     return;
   }
 
-  for (const parameter of parameters) {
-    const queryParam = query && query[parameter.slug];
+  parameters.forEach(parameter => {
+    const parameterValue = getParameterValueFromQueryParams(parameter, query);
 
-    if (queryParam != null || parameter.default != null) {
-      const parsedParam = parseQueryParams(queryParam, parameter, metadata);
-
-      setParameterValue(parameter.id, parsedParam);
+    if (hasParameterValue(parameterValue)) {
+      const parsedParameterValue = parseQueryParams(
+        parameterValue,
+        parameter,
+        metadata,
+      );
+      setParameterValue(parameter.id, parsedParameterValue);
     }
-  }
+  });
 };
 
 const syncForPublicQuestion = props => {
@@ -31,17 +39,17 @@ const syncForPublicQuestion = props => {
     return;
   }
 
-  const parameterValues = parameters.reduce((acc, parameter) => {
-    const queryParam = query && query[parameter.slug];
-
-    if (queryParam != null || parameter.default != null) {
-      acc[parameter.id] = parseQueryParams(queryParam, parameter, metadata);
-    }
+  const parameterIdValuePairs = getParameterValuePairsFromQueryParams(
+    parameters,
+    query,
+  ).map(([parameter, value]) => [
+    parameter.id,
+    parseQueryParams(value, parameter, metadata),
+  ]);
 
-    return acc;
-  }, {});
+  const parameterValuesById = Object.fromEntries(parameterIdValuePairs);
 
-  setMultipleParameterValues(parameterValues);
+  setMultipleParameterValues(parameterValuesById);
 };
 
 const parseQueryParams = (queryParam, parameter, metadata) => {
diff --git a/frontend/src/metabase/parameters/components/ParametersList.jsx b/frontend/src/metabase/parameters/components/ParametersList.jsx
index 6975d04068f18820e9bc5077eec103c1561e3297..0f3e00e5eabc59c31653c4d6a5f12e6ecc82dc84 100644
--- a/frontend/src/metabase/parameters/components/ParametersList.jsx
+++ b/frontend/src/metabase/parameters/components/ParametersList.jsx
@@ -9,7 +9,10 @@ import cx from "classnames";
 
 import StaticParameterWidget from "./ParameterWidget";
 import Icon from "metabase/components/Icon";
-import { collateParametersWithValues } from "metabase/meta/Parameter";
+import {
+  getValuePopulatedParameters,
+  getVisibleParameters,
+} from "metabase/meta/Parameter";
 
 import type {
   ParameterId,
@@ -105,15 +108,16 @@ function ParametersList({
     }
   };
 
-  const hiddenParameters =
-    typeof hideParameters === "string"
-      ? new Set(hideParameters.split(","))
-      : new Set();
-  const collatedParameters = collateParametersWithValues(
+  const valuePopulatedParameters = getValuePopulatedParameters(
     parameters,
     parameterValues,
   );
 
+  const visibleValuePopulatedParameters = getVisibleParameters(
+    valuePopulatedParameters,
+    hideParameters,
+  );
+
   let ParameterWidget;
   let ParameterWidgetList;
   if (isEditing) {
@@ -137,41 +141,42 @@ function ParametersList({
       onSortStart={handleSortStart}
       onSortEnd={handleSortEnd}
     >
-      {collatedParameters
-        .filter(p => !hiddenParameters.has(p.slug))
-        .map((parameter, index) => (
-          <ParameterWidget
-            key={parameter.id}
-            className={cx({ mb2: vertical })}
-            isEditing={isEditing}
-            isFullscreen={isFullscreen}
-            isNightMode={isNightMode}
-            parameter={parameter}
-            parameters={collatedParameters}
-            dashboard={dashboard}
-            editingParameter={editingParameter}
-            setEditingParameter={setEditingParameter}
-            index={index}
-            setName={
-              setParameterName && (name => setParameterName(parameter.id, name))
-            }
-            setValue={
-              setParameterValue &&
-              (value => setParameterValue(parameter.id, value))
-            }
-            setDefaultValue={
-              setParameterDefaultValue &&
-              (value => setParameterDefaultValue(parameter.id, value))
-            }
-            remove={removeParameter && (() => removeParameter(parameter.id))}
-            commitImmediately={commitImmediately}
-            dragHandle={
-              isEditing && setParameterIndex ? (
-                <SortableParameterHandle />
-              ) : null
-            }
-          />
-        ))}
+      {visibleValuePopulatedParameters.map((valuePopulatedParameter, index) => (
+        <ParameterWidget
+          key={valuePopulatedParameter.id}
+          className={cx({ mb2: vertical })}
+          isEditing={isEditing}
+          isFullscreen={isFullscreen}
+          isNightMode={isNightMode}
+          parameter={valuePopulatedParameter}
+          parameters={valuePopulatedParameters}
+          dashboard={dashboard}
+          editingParameter={editingParameter}
+          setEditingParameter={setEditingParameter}
+          index={index}
+          setName={
+            setParameterName &&
+            (name => setParameterName(valuePopulatedParameter.id, name))
+          }
+          setValue={
+            setParameterValue &&
+            (value => setParameterValue(valuePopulatedParameter.id, value))
+          }
+          setDefaultValue={
+            setParameterDefaultValue &&
+            (value =>
+              setParameterDefaultValue(valuePopulatedParameter.id, value))
+          }
+          remove={
+            removeParameter &&
+            (() => removeParameter(valuePopulatedParameter.id))
+          }
+          commitImmediately={commitImmediately}
+          dragHandle={
+            isEditing && setParameterIndex ? <SortableParameterHandle /> : null
+          }
+        />
+      ))}
     </ParameterWidgetList>
   );
 }
diff --git a/frontend/src/metabase/public/components/EmbedFrame.jsx b/frontend/src/metabase/public/components/EmbedFrame.jsx
index 3b826c9a506997b1ed408b22a96cc3b9bda24c64..c19d5c42c3420960e0937c30b345bd80ddd41cad 100644
--- a/frontend/src/metabase/public/components/EmbedFrame.jsx
+++ b/frontend/src/metabase/public/components/EmbedFrame.jsx
@@ -5,6 +5,7 @@ import { IFRAMED, initializeIframeResizer } from "metabase/lib/dom";
 import { parseHashOptions } from "metabase/lib/browser";
 
 import MetabaseSettings from "metabase/lib/settings";
+import { getValuePopulatedParameters } from "metabase/meta/Parameter";
 
 import TitleAndDescription from "metabase/components/TitleAndDescription";
 import Parameters from "metabase/parameters/components/Parameters/Parameters";
@@ -98,10 +99,10 @@ export default class EmbedFrame extends Component {
                 <div className="flex ml-auto">
                   <Parameters
                     dashboard={this.props.dashboard}
-                    parameters={parameters.map(p => ({
-                      ...p,
-                      value: parameterValues && parameterValues[p.id],
-                    }))}
+                    parameters={getValuePopulatedParameters(
+                      parameters,
+                      parameterValues,
+                    )}
                     query={location.query}
                     setParameterValue={setParameterValue}
                     setMultipleParameterValues={setMultipleParameterValues}
diff --git a/frontend/src/metabase/public/components/widgets/EmbedModalContent.jsx b/frontend/src/metabase/public/components/widgets/EmbedModalContent.jsx
index 2b97bd9c6abc2074fc4bd32bd5644889881835fe..40bca0a0d4f19f4e85447ec88622292e9f7d5801 100644
--- a/frontend/src/metabase/public/components/widgets/EmbedModalContent.jsx
+++ b/frontend/src/metabase/public/components/widgets/EmbedModalContent.jsx
@@ -1,7 +1,6 @@
 import React, { Component } from "react";
 import { connect } from "react-redux";
 import { titleize } from "inflection";
-
 import { t } from "ttag";
 
 import Icon from "metabase/components/Icon";
@@ -132,19 +131,30 @@ export default class EmbedModalContent extends Component {
     this.setState({ embeddingParams: resource.embedding_params || {} });
   };
 
-  getPreviewParams() {
+  getPreviewParameters(resourceParameters, embeddingParams) {
+    const lockedParameters = resourceParameters.filter(
+      parameter => embeddingParams[parameter.slug] === "locked",
+    );
+
+    return lockedParameters;
+  }
+
+  getPreviewParamsBySlug() {
     const { resourceParameters } = this.props;
     const { embeddingParams, parameterValues } = this.state;
-    const params = {};
-    for (const parameter of resourceParameters) {
-      if (embeddingParams[parameter.slug] === "locked") {
-        params[parameter.slug] =
-          parameter.id in parameterValues
-            ? parameterValues[parameter.id]
-            : null;
-      }
-    }
-    return params;
+
+    const lockedParameters = this.getPreviewParameters(
+      resourceParameters,
+      embeddingParams,
+    );
+
+    const parameterSlugValuePairs = lockedParameters.map(parameter => {
+      const value =
+        parameter.id in parameterValues ? parameterValues[parameter.id] : null;
+      return [parameter.slug, value];
+    });
+
+    return Object.fromEntries(parameterSlugValuePairs);
   }
 
   render() {
@@ -164,10 +174,10 @@ export default class EmbedModalContent extends Component {
       displayOptions,
     } = this.state;
 
-    const params = this.getPreviewParams();
-
-    const previewParameters = resourceParameters.filter(
-      p => embeddingParams[p.slug] === "locked",
+    const previewParametersBySlug = this.getPreviewParamsBySlug();
+    const previewParameters = this.getPreviewParameters(
+      resourceParameters,
+      embeddingParams,
     );
 
     return (
@@ -229,7 +239,7 @@ export default class EmbedModalContent extends Component {
               token={getSignedToken(
                 resourceType,
                 resource.id,
-                params,
+                previewParametersBySlug,
                 secretKey,
                 embeddingParams,
               )}
@@ -237,14 +247,14 @@ export default class EmbedModalContent extends Component {
                 siteUrl,
                 resourceType,
                 resource.id,
-                params,
+                previewParametersBySlug,
                 displayOptions,
                 secretKey,
                 embeddingParams,
               )}
               siteUrl={siteUrl}
               secretKey={secretKey}
-              params={params}
+              params={previewParametersBySlug}
               displayOptions={displayOptions}
               previewParameters={previewParameters}
               parameterValues={parameterValues}
diff --git a/frontend/src/metabase/public/containers/PublicQuestion.jsx b/frontend/src/metabase/public/containers/PublicQuestion.jsx
index c6b882e892edbf63b67bae7769f39fad857e8124..c4df0f6d2f37208b4f533effaf416b4c33182a05 100644
--- a/frontend/src/metabase/public/containers/PublicQuestion.jsx
+++ b/frontend/src/metabase/public/containers/PublicQuestion.jsx
@@ -13,10 +13,13 @@ import type { Card } from "metabase-types/types/Card";
 import type { Dataset } from "metabase-types/types/Dataset";
 import type { ParameterValues } from "metabase-types/types/Parameter";
 
-import { getParametersBySlug } from "metabase/meta/Parameter";
 import {
-  getParameters,
-  getParametersWithExtras,
+  getParameterValuesBySlug,
+  getParameterValuesByIdFromQueryParams,
+} from "metabase/meta/Parameter";
+import {
+  getParametersFromCard,
+  getValueAndFieldIdPopulatedParametersFromCard,
   applyParameters,
 } from "metabase/meta/Card";
 
@@ -112,15 +115,19 @@ export default class PublicQuestion extends Component {
         this.props.addFields(card.param_fields);
       }
 
-      const parameterValues: ParameterValues = {};
-      for (const parameter of getParameters(card)) {
-        parameterValues[String(parameter.id)] = query[parameter.slug];
-      }
+      const parameters = getParametersFromCard(card);
+      const parameterValuesById = getParameterValuesByIdFromQueryParams(
+        parameters,
+        query,
+      );
 
-      this.setState({ card, parameterValues }, async () => {
-        await this.run();
-        this.setState({ initialized: true });
-      });
+      this.setState(
+        { card, parameterValues: parameterValuesById },
+        async () => {
+          await this.run();
+          this.setState({ initialized: true });
+        },
+      );
     } catch (error) {
       console.error("error", error);
       setErrorPage(error);
@@ -162,7 +169,7 @@ export default class PublicQuestion extends Component {
       return;
     }
 
-    const parameters = getParameters(card);
+    const parameters = getParametersFromCard(card);
 
     try {
       this.setState({ result: null });
@@ -172,7 +179,7 @@ export default class PublicQuestion extends Component {
         // embeds apply parameter values server-side
         newResult = await maybeUsePivotEndpoint(EmbedApi.cardQuery, card)({
           token,
-          ...getParametersBySlug(parameters, parameterValues),
+          ...getParameterValuesBySlug(parameters, parameterValues),
         });
       } else if (uuid) {
         // public links currently apply parameters client-side
@@ -207,7 +214,8 @@ export default class PublicQuestion extends Component {
       />
     );
 
-    const parameters = card && getParametersWithExtras(card);
+    const parameters =
+      card && getValueAndFieldIdPopulatedParametersFromCard(card);
 
     return (
       <EmbedFrame
diff --git a/frontend/src/metabase/query_builder/containers/QuestionEmbedWidget.jsx b/frontend/src/metabase/query_builder/containers/QuestionEmbedWidget.jsx
index acc734fc9893a537c8621c82513cbc22699c2a7d..8b5683b55b37205f01729ee8396584857cb47e02 100644
--- a/frontend/src/metabase/query_builder/containers/QuestionEmbedWidget.jsx
+++ b/frontend/src/metabase/query_builder/containers/QuestionEmbedWidget.jsx
@@ -12,7 +12,7 @@ import * as Urls from "metabase/lib/urls";
 import MetabaseSettings from "metabase/lib/settings";
 import MetabaseAnalytics from "metabase/lib/analytics";
 
-import { getParameters } from "metabase/meta/Card";
+import { getParametersFromCard } from "metabase/meta/Card";
 import {
   createPublicLink,
   deletePublicLink,
@@ -61,7 +61,7 @@ export default class QuestionEmbedWidget extends Component {
         className={className}
         resource={card}
         resourceType="question"
-        resourceParameters={getParameters(card)}
+        resourceParameters={getParametersFromCard(card)}
         onCreatePublicLink={() => createPublicLink(card)}
         onDisablePublicLink={() => deletePublicLink(card)}
         onUpdateEnableEmbedding={enableEmbedding =>
diff --git a/frontend/src/metabase/query_builder/selectors.js b/frontend/src/metabase/query_builder/selectors.js
index 17fcdeb173ea7871fbaff2da19ad82205fbec1a4..5f6f3016da30646444dd03a6aa89337286820fae 100644
--- a/frontend/src/metabase/query_builder/selectors.js
+++ b/frontend/src/metabase/query_builder/selectors.js
@@ -13,7 +13,7 @@ import {
   getVisualizationTransformed,
 } from "metabase/visualizations";
 import { getComputedSettingsForSeries } from "metabase/visualizations/lib/settings/visualization";
-import { getParametersWithExtras } from "metabase/meta/Card";
+import { getValueAndFieldIdPopulatedParametersFromCard } from "metabase/meta/Card";
 import { normalizeParameterValue } from "metabase/meta/Parameter";
 
 import Utils from "metabase/lib/utils";
@@ -118,7 +118,8 @@ export const getDatabaseFields = createSelector(
 
 export const getParameters = createSelector(
   [getCard, getParameterValues],
-  (card, parameterValues) => getParametersWithExtras(card, parameterValues),
+  (card, parameterValues) =>
+    getValueAndFieldIdPopulatedParametersFromCard(card, parameterValues),
 );
 
 const getLastRunDatasetQuery = createSelector(
diff --git a/frontend/test/metabase/meta/Parameter.unit.spec.js b/frontend/test/metabase/meta/Parameter.unit.spec.js
index 57a4307305524aa479ff14ca8bb6f8d96ecde162..be2953b72b2a16007ed812306771038d2a8f7d4e 100644
--- a/frontend/test/metabase/meta/Parameter.unit.spec.js
+++ b/frontend/test/metabase/meta/Parameter.unit.spec.js
@@ -4,10 +4,16 @@ import {
   stringParameterValueToMBQL,
   numberParameterValueToMBQL,
   parameterOptionsForField,
-  getParametersBySlug,
   normalizeParameterValue,
   deriveFieldOperatorFromParameter,
   getTemplateTagParameters,
+  getValuePopulatedParameters,
+  getParameterValueFromQueryParams,
+  getParameterValuePairsFromQueryParams,
+  getParameterValuesByIdFromQueryParams,
+  getParameterValuesBySlug,
+  buildHiddenParametersSlugSet,
+  getVisibleParameters,
 } from "metabase/meta/Parameter";
 
 MetabaseSettings.get = jest.fn();
@@ -212,25 +218,6 @@ describe("metabase/meta/Parameter", () => {
     });
   });
 
-  describe("getParameterBySlug", () => {
-    it("should return an object mapping slug to parameter value", () => {
-      const parameters = [
-        { id: "foo", slug: "bar" },
-        { id: "aaa", slug: "bbb" },
-        { id: "cat", slug: "dog" },
-      ];
-      const parameterValuesById = {
-        foo: 123,
-        aaa: "ccc",
-        something: true,
-      };
-      expect(getParametersBySlug(parameters, parameterValuesById)).toEqual({
-        bar: 123,
-        bbb: "ccc",
-      });
-    });
-  });
-
   describe("normalizeParameterValue", () => {
     it("should return same value for location/category parameters", () => {
       expect(normalizeParameterValue("category", "foo")).toEqual("foo");
@@ -446,4 +433,225 @@ describe("metabase/meta/Parameter", () => {
       });
     });
   });
+
+  describe("parameter collection-building utils", () => {
+    // found in queryParams and not defaulted
+    const parameter1 = {
+      id: 1,
+      slug: "foo",
+    };
+    // found in queryParams and defaulted
+    const parameter2 = {
+      id: 2,
+      slug: "bar",
+      default: "parameter2 default value",
+    };
+    // not found in queryParams and defaulted
+    const parameter3 = {
+      id: 3,
+      slug: "baz",
+      default: "parameter3 default value",
+    };
+    // not found in queryParams and not defaulted
+    const parameter4 = {
+      id: 4,
+      slug: "qux",
+    };
+    const parameters = [parameter1, parameter2, parameter3, parameter4];
+    const queryParams = {
+      foo: "parameter1 queryParam value",
+      bar: "parameter2 queryParam value",
+      valueNotFoundInParameters: "nonexistent parameter queryParam value",
+    };
+
+    const parameterValues = getParameterValuesByIdFromQueryParams(
+      parameters,
+      queryParams,
+    );
+
+    describe("getValuePopulatedParameters", () => {
+      it("should return an array of parameter objects with the `value` property set if it exists in the given `parameterValues` id, value map", () => {
+        expect(
+          getValuePopulatedParameters(parameters, {
+            [parameter1.id]: "parameter1 value",
+            [parameter2.id]: "parameter2 value",
+          }),
+        ).toEqual([
+          {
+            ...parameter1,
+            value: "parameter1 value",
+          },
+          {
+            ...parameter2,
+            value: "parameter2 value",
+          },
+          parameter3,
+          parameter4,
+        ]);
+      });
+
+      it("should handle there being an undefined or null parameterValues object", () => {
+        expect(getValuePopulatedParameters(parameters, undefined)).toEqual(
+          parameters,
+        );
+        expect(getValuePopulatedParameters(parameters, null)).toEqual(
+          parameters,
+        );
+      });
+    });
+
+    describe("getParameterValueFromQueryParams", () => {
+      it("should return undefined when given an undefined queryParams arg", () => {
+        expect(getParameterValueFromQueryParams(parameter1)).toBe(undefined);
+      });
+
+      it("should return the parameter's default value when given an undefined queryParams arg", () => {
+        expect(getParameterValueFromQueryParams(parameter2)).toBe(
+          "parameter2 default value",
+        );
+      });
+
+      it("should return the parameter's default value when the parameter value is not found in queryParams", () => {
+        expect(getParameterValueFromQueryParams(parameter3, queryParams)).toBe(
+          "parameter3 default value",
+        );
+      });
+
+      it("should return the parameter value found in the queryParams object", () => {
+        expect(getParameterValueFromQueryParams(parameter1, queryParams)).toBe(
+          "parameter1 queryParam value",
+        );
+      });
+
+      it("should ignore the parameter's default value when the parameter value is found in queryParams", () => {
+        expect(getParameterValueFromQueryParams(parameter2, queryParams)).toBe(
+          "parameter2 queryParam value",
+        );
+      });
+    });
+
+    describe("getParameterValuePairsFromQueryParams", () => {
+      it("should build a list of parameter and parameter value pairs", () => {
+        expect(
+          getParameterValuePairsFromQueryParams(parameters, queryParams),
+        ).toEqual([
+          [parameter1, "parameter1 queryParam value"],
+          [parameter2, "parameter2 queryParam value"],
+          [parameter3, "parameter3 default value"],
+        ]);
+      });
+
+      it("should handle undefined queryParams", () => {
+        expect(getParameterValuePairsFromQueryParams(parameters)).toEqual([
+          [parameter2, "parameter2 default value"],
+          [parameter3, "parameter3 default value"],
+        ]);
+      });
+    });
+
+    describe("getParameterValuesByIdFromQueryParams", () => {
+      it("should generate a map of parameter values found in the queryParams or with default values", () => {
+        expect(
+          getParameterValuesByIdFromQueryParams(parameters, queryParams),
+        ).toEqual({
+          [parameter1.id]: "parameter1 queryParam value",
+          [parameter2.id]: "parameter2 queryParam value",
+          [parameter3.id]: "parameter3 default value",
+        });
+      });
+
+      it("should handle an undefined queryParams", () => {
+        expect(getParameterValuesByIdFromQueryParams(parameters)).toEqual({
+          [parameter2.id]: "parameter2 default value",
+          [parameter3.id]: "parameter3 default value",
+        });
+      });
+    });
+
+    describe("getParameterValuesBySlug", () => {
+      it("should return a map of defined parameter values keyed by the parameter's slug", () => {
+        expect(getParameterValuesBySlug(parameters, parameterValues)).toEqual({
+          [parameter1.slug]: "parameter1 queryParam value",
+          [parameter2.slug]: "parameter2 queryParam value",
+          [parameter3.slug]: "parameter3 default value",
+        });
+      });
+
+      it("should prioritize values found on the parameter object over the parameterValues map", () => {
+        const valuePopulatedParameter1 = {
+          ...parameter1,
+          value: "parameter1 value prop",
+        };
+        const parameters = [valuePopulatedParameter1, parameter2];
+
+        expect(getParameterValuesBySlug(parameters, parameterValues)).toEqual({
+          [parameter1.slug]: "parameter1 value prop",
+          [parameter2.slug]: "parameter2 queryParam value",
+        });
+      });
+
+      it("should handle an undefined parameterValues map", () => {
+        expect(getParameterValuesBySlug(parameters, undefined)).toEqual({});
+        expect(
+          getParameterValuesBySlug([
+            {
+              ...parameter1,
+              value: "parameter1 value prop",
+            },
+          ]),
+        ).toEqual({
+          [parameter1.slug]: "parameter1 value prop",
+        });
+      });
+    });
+  });
+
+  describe("buildHiddenParametersSlugSet", () => {
+    it("should turn the given string of slugs separated by commas into a set of slug strings", () => {
+      expect(buildHiddenParametersSlugSet("a,b,c")).toEqual(
+        new Set(["a", "b", "c"]),
+      );
+    });
+
+    it("should return an empty set for any input that is not a string", () => {
+      expect(buildHiddenParametersSlugSet(undefined)).toEqual(new Set());
+      expect(buildHiddenParametersSlugSet(111111)).toEqual(new Set());
+    });
+  });
+
+  describe("getVisibleParameters", () => {
+    const parameters = [
+      {
+        id: 1,
+        slug: "foo",
+      },
+      {
+        id: 2,
+        slug: "bar",
+      },
+      {
+        id: 3,
+        slug: "baz",
+      },
+      {
+        id: 4,
+        slug: "qux",
+      },
+    ];
+
+    const hiddenParameterSlugs = "bar,baz";
+
+    it("should return the parameters that are not hidden", () => {
+      expect(getVisibleParameters(parameters, hiddenParameterSlugs)).toEqual([
+        {
+          id: 1,
+          slug: "foo",
+        },
+        {
+          id: 4,
+          slug: "qux",
+        },
+      ]);
+    });
+  });
 });