diff --git a/commerce_shipping.install b/commerce_shipping.install
index 22698e6..d2d8202 100644
--- a/commerce_shipping.install
+++ b/commerce_shipping.install
@@ -21,3 +21,24 @@ function commerce_shipping_update_8200() {
     ->setDisplayConfigurable('view', TRUE);
   $entity_definition_update->uninstallFieldStorageDefinition($storage_definition);
 }
+
+/**
+ * Add the 'conditions' field to shipping methods.
+ */
+function commerce_shipping_update_8201() {
+  $entity_definition_update = \Drupal::entityDefinitionUpdateManager();
+
+  $storage_definition = BaseFieldDefinition::create('commerce_plugin_item:commerce_condition')
+    ->setLabel(t('Conditions'))
+    ->setTargetEntityTypeId('commerce_shipping_method')
+    ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
+    ->setRequired(FALSE)
+    ->setDisplayOptions('form', [
+      'type' => 'commerce_conditions',
+      'weight' => 3,
+      'settings' => [
+        'entity_types' => ['commerce_order', 'commerce_shipment'],
+      ],
+    ]);
+  $entity_definition_update->installFieldStorageDefinition('conditions', 'commerce_shipping_method', 'commerce_shipping', $storage_definition);
+}
diff --git a/src/Entity/ShippingMethod.php b/src/Entity/ShippingMethod.php
index 480a4e7..7d0bb72 100644
--- a/src/Entity/ShippingMethod.php
+++ b/src/Entity/ShippingMethod.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\commerce_shipping\Entity;
 
+use Drupal\commerce\ConditionGroup;
 use Drupal\Core\Entity\ContentEntityBase;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
@@ -114,6 +115,18 @@ class ShippingMethod extends ContentEntityBase implements ShippingMethodInterfac
   /**
    * {@inheritdoc}
    */
+  public function getConditions() {
+    $conditions = [];
+    foreach ($this->get('conditions') as $field_item) {
+      /** @var \Drupal\commerce\Plugin\Field\FieldType\PluginItemInterface $field_item */
+      $conditions[] = $field_item->getTargetInstance();
+    }
+    return $conditions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getWeight() {
     return (int) $this->get('weight')->value;
   }
@@ -142,6 +155,29 @@ class ShippingMethod extends ContentEntityBase implements ShippingMethodInterfac
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function applies(ShipmentInterface $shipment) {
+    $conditions = $this->getConditions();
+    if (!$conditions) {
+      // Shipping methods without conditions always apply.
+      return TRUE;
+    }
+    $order_conditions = array_filter($conditions, function ($condition) {
+      /** @var \Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface $condition */
+      return $condition->getEntityTypeId() == 'commerce_order';
+    });
+    $shipment_conditions = array_filter($conditions, function ($condition) {
+      /** @var \Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface $condition */
+      return $condition->getEntityTypeId() == 'commerce_shipment';
+    });
+    $order_conditions = new ConditionGroup($order_conditions, 'AND');
+    $shipment_conditions = new ConditionGroup($shipment_conditions, 'AND');
+
+    return $order_conditions->evaluate($shipment->getOrder()) && $shipment_conditions->evaluate($shipment);
+  }
+
+  /**
    * Helper callback for uasort() to sort shipping methods by weight and label.
    *
    * @param \Drupal\commerce_shipping\Entity\ShippingMethodInterface $a
@@ -206,6 +242,18 @@ class ShippingMethod extends ContentEntityBase implements ShippingMethodInterfac
       ->setDisplayConfigurable('view', TRUE)
       ->setDisplayConfigurable('form', TRUE);
 
+    $fields['conditions'] = BaseFieldDefinition::create('commerce_plugin_item:commerce_condition')
+      ->setLabel(t('Conditions'))
+      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
+      ->setRequired(FALSE)
+      ->setDisplayOptions('form', [
+        'type' => 'commerce_conditions',
+        'weight' => 3,
+        'settings' => [
+          'entity_types' => ['commerce_order', 'commerce_shipment'],
+        ],
+      ]);
+
     $fields['weight'] = BaseFieldDefinition::create('integer')
       ->setLabel(t('Weight'))
       ->setDescription(t('The weight of this shipping method in relation to others.'))
diff --git a/src/Entity/ShippingMethodInterface.php b/src/Entity/ShippingMethodInterface.php
index 2702a44..f5b65d4 100644
--- a/src/Entity/ShippingMethodInterface.php
+++ b/src/Entity/ShippingMethodInterface.php
@@ -41,6 +41,14 @@ interface ShippingMethodInterface extends ContentEntityInterface, EntityStoresIn
   public function setName($name);
 
   /**
+   * Gets the shipping method conditions.
+   *
+   * @return \Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface[]
+   *   The shipping method conditions.
+   */
+  public function getConditions();
+
+  /**
    * Gets the shipping method weight.
    *
    * @return string
@@ -76,4 +84,17 @@ interface ShippingMethodInterface extends ContentEntityInterface, EntityStoresIn
    */
   public function setEnabled($enabled);
 
+  /**
+   * Checks whether the shipping method applies to the given shipment.
+   *
+   * Ensures that the conditions pass.
+   *
+   * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment
+   *   The shipment.
+   *
+   * @return bool
+   *   TRUE if shipping method applies, FALSE otherwise.
+   */
+  public function applies(ShipmentInterface $shipment);
+
 }
diff --git a/src/Plugin/Commerce/Condition/ShipmentShippingAddress.php b/src/Plugin/Commerce/Condition/ShipmentShippingAddress.php
new file mode 100644
index 0000000..eda9c2c
--- /dev/null
+++ b/src/Plugin/Commerce/Condition/ShipmentShippingAddress.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\commerce_shipping\Plugin\Commerce\Condition;
+
+use CommerceGuys\Addressing\Zone\Zone;
+use Drupal\commerce\Plugin\Commerce\Condition\ConditionBase;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides the shipping address condition for orders.
+ *
+ * @CommerceCondition(
+ *   id = "shipment_shipping_address",
+ *   label = @Translation("Shipping address"),
+ *   display_label = @Translation("Limit by shipping address"),
+ *   category = @Translation("Customer"),
+ *   entity_type = "commerce_shipment",
+ * )
+ */
+class ShipmentShippingAddress extends ConditionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'zone' => NULL,
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    $form['zone'] = [
+      '#type' => 'address_zone',
+      '#default_value' => $this->configuration['zone'],
+      '#required' => TRUE,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    parent::submitConfigurationForm($form, $form_state);
+
+    $values = $form_state->getValue($form['#parents']);
+    // Work around an Address bug where the Remove button value is kept in the
+    // array.
+    foreach ($values['zone']['territories'] as &$territory) {
+      unset($territory['remove']);
+    }
+    // Don't store the label, it's always hidden and empty.
+    unset($values['zone']['label']);
+
+    $this->configuration['zone'] = $values['zone'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function evaluate(EntityInterface $entity) {
+    $this->assertEntity($entity);
+    /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment */
+    $shipment = $entity;
+    $shipping_profile = $shipment->getShippingProfile();
+    if (!$shipping_profile) {
+      // The conditions can't be applied until the shipping address is known.
+      return FALSE;
+    }
+    $zone = new Zone([
+      'id' => 'shipping',
+      'label' => 'N/A',
+    ] + $this->configuration['zone']);
+    $address = $shipping_profile->get('address')->first();
+
+    return $zone->match($address);
+  }
+
+}
diff --git a/src/Plugin/Commerce/Condition/ShipmentWeight.php b/src/Plugin/Commerce/Condition/ShipmentWeight.php
new file mode 100644
index 0000000..11661ac
--- /dev/null
+++ b/src/Plugin/Commerce/Condition/ShipmentWeight.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace Drupal\commerce_shipping\Plugin\Commerce\Condition;
+
+use Drupal\commerce\Plugin\Commerce\Condition\ConditionBase;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\physical\Weight;
+use Drupal\physical\MeasurementType;
+
+/**
+ * Provides the shipping address condition for orders.
+ *
+ * @CommerceCondition(
+ *   id = "shipment_weight",
+ *   label = @Translation("Shipment Weight"),
+ *   display_label = @Translation("Limit by shipment weight"),
+ *   category = @Translation("Shipment"),
+ *   entity_type = "commerce_shipment",
+ * )
+ */
+class ShipmentWeight extends ConditionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'operator' => '>',
+      'amount' => NULL,
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    $weight = $this->configuration['weight'];
+
+    $form['operator'] = [
+      '#type' => 'select',
+      '#title' => t('Operator'),
+      '#options' => $this->getComparisonOperators(),
+      '#default_value' => $this->configuration['operator'],
+      '#required' => TRUE,
+    ];
+    $form['weight'] = [
+      '#type' => 'physical_measurement',
+      '#measurement_type' => MeasurementType::WEIGHT,
+      '#title' => t('Weight'),
+      '#default_value' => $weight,
+      '#required' => TRUE,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    parent::submitConfigurationForm($form, $form_state);
+
+    $values = $form_state->getValue($form['#parents']);
+    $this->configuration['operator'] = $values['operator'];
+    $this->configuration['weight'] = $values['weight'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function evaluate(EntityInterface $entity) {
+    $this->assertEntity($entity);
+    /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment */
+    $shipment = $entity;
+    $weight = $shipment->getWeight();
+    if (!$weight) {
+      // The conditions can't be applied until the weight is known.
+      return FALSE;
+    }
+
+    $condition_unit = $this->configuration['weight']['unit'];
+
+    $weight = $weight->convert($condition_unit);
+
+    $condition_weight = new Weight($this->configuration['weight']['number'], $condition_unit);
+
+    switch ($this->configuration['operator']) {
+      case '>=':
+        return $weight->greaterThanOrEqual($condition_weight);
+
+      case '>':
+        return $weight->greaterThan($condition_weight);
+
+      case '<=':
+        return $weight->lessThanOrEqual($condition_weight);
+
+      case '<':
+        return $weight->lessThan($condition_weight);
+
+      case '==':
+        return $weight->equals($condition_weight);
+
+      default:
+        throw new \InvalidArgumentException("Invalid operator {$this->configuration['operator']}");
+    }
+
+  }
+
+}
diff --git a/src/ShippingMethodStorage.php b/src/ShippingMethodStorage.php
index 24b3b0c..c5918f3 100644
--- a/src/ShippingMethodStorage.php
+++ b/src/ShippingMethodStorage.php
@@ -20,11 +20,15 @@ class ShippingMethodStorage extends CommerceContentEntityStorage implements Ship
       ->condition('status', TRUE);
     $result = $query->execute();
     $shipping_methods = $result ? $this->loadMultiple($result) : [];
+    foreach ($shipping_methods as $shipping_method_id => $shipping_method) {
+      if (!$shipping_method->applies($shipment)) {
+        unset($shipping_methods[$shipping_method_id]);
+      }
+    }
+    uasort($shipping_methods, [$this->entityType->getClass(), 'sort']);
     if (!empty($shipping_methods)) {
-      uasort($shipping_methods, [$this->entityType->getClass(), 'sort']);
       // Allow modules to alter the list of available shipping methods via
-      // hook_commerce_shipping_methods_alter(&$shipping_methods, $shipment),
-      // as a stop-gap measure until conditions are implemented.
+      // hook_commerce_shipping_methods_alter(&$shipping_methods, $shipment).
       \Drupal::moduleHandler()->alter('commerce_shipping_methods', $shipping_methods, $shipment);
     }
 
