diff --git a/lib/combine.dart b/lib/combine.dart
new file mode 100644
index 00000000..0fd92b97
--- /dev/null
+++ b/lib/combine.dart
@@ -0,0 +1,4 @@
+library turf_combine;
+
+export 'package:geotypes/geotypes.dart';
+export 'src/combine.dart';
diff --git a/lib/src/combine.dart b/lib/src/combine.dart
new file mode 100644
index 00000000..aad121fa
--- /dev/null
+++ b/lib/src/combine.dart
@@ -0,0 +1,111 @@
+import 'package:turf/meta.dart';
+
+/// Combines a [FeatureCollection] of Point, LineString or Polygon features
+/// into a single MultiPoint, MultiLineString or MultiPolygon feature.
+///
+/// The [collection] must be a FeatureCollection of the same geometry type.
+/// Supported types are Point, LineString, and Polygon.
+///
+/// Returns a [Feature] with a Multi* geometry containing all coordinates from the input collection.
+/// Throws [ArgumentError] if features have inconsistent geometry types or unsupported types.
+///
+/// If [mergeProperties] is true, properties from the first feature will be preserved.
+/// Otherwise, properties will be empty by default.
+///
+/// See: https://turfjs.org/docs/#combine
+Feature combine(
+  FeatureCollection collection, {
+  bool mergeProperties = false,
+}) {
+  // Validate that the collection is not empty
+  if (collection.features.isEmpty) {
+    throw ArgumentError('FeatureCollection must contain at least one feature');
+  }
+
+  // Get the geometry type of the first feature to validate consistency
+  final firstFeature = collection.features.first;
+  final geometryType = firstFeature.geometry?.runtimeType;
+  if (geometryType == null) {
+    throw ArgumentError('Feature must have a geometry');
+  }
+  
+  final firstGeometry = firstFeature.geometry!;
+
+  // Ensure all features have the same geometry type
+  for (final feature in collection.features) {
+    final geometry = feature.geometry;
+    if (geometry == null) {
+      throw ArgumentError('All features must have a geometry');
+    }
+    
+    if (geometry.runtimeType != firstGeometry.runtimeType) {
+      throw ArgumentError(
+        'All features must have the same geometry type. '
+        'Found: ${geometry.type}, expected: ${firstGeometry.type}',
+      );
+    }
+  }
+
+  // Set of properties to include in result if mergeProperties is true
+  final properties = mergeProperties && firstFeature.properties != null 
+      ? Map<String, dynamic>.from(firstFeature.properties!)
+      : <String, dynamic>{};
+
+  // Create the appropriate geometry based on type
+  GeometryObject resultGeometry;
+  
+  if (firstGeometry is Point) {
+    // Combine all Point coordinates into a single MultiPoint
+    final coordinates = <Position>[];
+    for (final feature in collection.features) {
+      final point = feature.geometry as Point;
+      coordinates.add(point.coordinates);
+    }
+    
+    resultGeometry = MultiPoint(coordinates: coordinates);
+  } else if (firstGeometry is LineString) {
+    // Combine all LineString coordinate arrays into a MultiLineString
+    final coordinates = <List<Position>>[];
+    for (final feature in collection.features) {
+      final line = feature.geometry as LineString;
+      coordinates.add(line.coordinates);
+    }
+    
+    resultGeometry = MultiLineString(coordinates: coordinates);
+  } else if (firstGeometry is Polygon) {
+    // Combine all Polygon coordinate arrays into a MultiPolygon
+    final coordinates = <List<List<Position>>>[];
+    for (final feature in collection.features) {
+      final polygon = feature.geometry as Polygon;
+      coordinates.add(polygon.coordinates);
+    }
+    
+    resultGeometry = MultiPolygon(coordinates: coordinates);
+  } else {
+    // Throw if unsupported geometry type is encountered
+    throw ArgumentError(
+      'Unsupported geometry type: ${firstGeometry.type}. '
+      'Only Point, LineString, and Polygon are supported.',
+    );
+  }
+
+  // Create the Feature result
+  final result = Feature(
+    geometry: resultGeometry,
+    properties: properties,
+  );
+  
+  // Apply otherMembers from the first feature to preserve GeoJSON compliance
+  final resultJson = result.toJson();
+  final firstFeatureJson = firstFeature.toJson();
+  
+  // Copy any non-standard GeoJSON fields (otherMembers)
+  firstFeatureJson.forEach((key, value) {
+    if (key != 'type' && key != 'geometry' && key != 'properties' && key != 'id') {
+      resultJson[key] = value;
+    }
+  });
+  
+  // Return the result with otherMembers preserved
+  return Feature.fromJson(resultJson);
+}
diff --git a/lib/turf.dart b/lib/turf.dart
index 482694bb..03d091d9 100644
--- a/lib/turf.dart
+++ b/lib/turf.dart
@@ -9,6 +9,7 @@ export 'bearing.dart';
 export 'boolean.dart';
 export 'center.dart';
 export 'centroid.dart';
+export 'combine.dart';
 export 'clean_coords.dart';
 export 'clusters.dart';
 export 'destination.dart';
diff --git a/test/components/combine_test.dart b/test/components/combine_test.dart
new file mode 100644
index 00000000..f9dcd1ed
--- /dev/null
+++ b/test/components/combine_test.dart
@@ -0,0 +1,258 @@
+import 'dart:convert';
+
+import 'package:geotypes/geotypes.dart';
+import 'package:test/test.dart';
+import 'package:turf/src/combine.dart';
+
+void main() {
+  group('combine:', () {
+    // Geometry-based tests
+    group('geometry transformations:', () {
+      test('combines multiple points to a MultiPoint', () {
+        final point1 = Feature(
+          geometry: Point(coordinates: Position.of([0, 0])),
+          properties: {'name': 'point1'},
+        );
+        final point2 = Feature(
+          geometry: Point(coordinates: Position.of([1, 1])),
+          properties: {'name': 'point2'},
+        );
+        final point3 = Feature(
+          geometry: Point(coordinates: Position.of([2, 2, 10])), // With altitude
+          properties: {'name': 'point3'},
+        );
+
+        final collection = FeatureCollection(features: [point1, point2, point3]);
+        final result = combine(collection);
+
+        expect(result.geometry, isA<MultiPoint>());
+        expect((result.geometry as MultiPoint).coordinates.length, 3);
+        // Check altitude preservation
+        expect((result.geometry as MultiPoint).coordinates[2].length, 3);
+        expect((result.geometry as MultiPoint).coordinates[2][2], 10);
+      });
+
+      test('combines multiple linestrings to a MultiLineString', () {
+        final line1 = Feature(
+          geometry: LineString(coordinates: [
+            Position.of([0, 0]),
+            Position.of([1, 1]),
+          ]),
+          properties: {'name': 'line1'},
+        );
+        final line2 = Feature(
+          geometry: LineString(coordinates: [
+            Position.of([2, 2]),
+            Position.of([3, 3]),
+          ]),
+          properties: {'name': 'line2'},
+        );
+        final line3 = Feature(
+          geometry: LineString(coordinates: [
+            Position.of([4, 4, 10]), // With altitude
+            Position.of([5, 5, 15]), // With altitude
+          ]),
+          properties: {'name': 'line3'},
+        );
+
+        final collection = FeatureCollection(features: [line1, line2, line3]);
+        final result = combine(collection);
+
+        expect(result.geometry, isA<MultiLineString>());
+        expect((result.geometry as MultiLineString).coordinates.length, 3);
+        // Check altitude preservation
+        expect((result.geometry as MultiLineString).coordinates[2][0].length, 3);
+        expect((result.geometry as MultiLineString).coordinates[2][0][2], 10);
+        expect((result.geometry as MultiLineString).coordinates[2][1][2], 15);
+      });
+
+      test('combines multiple polygons to a MultiPolygon', () {
+        final poly1 = Feature(
+          geometry: Polygon(coordinates: [
+            [
+              Position.of([0, 0]),
+              Position.of([1, 0]),
+              Position.of([1, 1]),
+              Position.of([0, 1]),
+              Position.of([0, 0]),
+            ]
+          ]),
+          properties: {'name': 'poly1'},
+        );
+        final poly2 = Feature(
+          geometry: Polygon(coordinates: [
+            [
+              Position.of([2, 2]),
+              Position.of([3, 2]),
+              Position.of([3, 3]),
+              Position.of([2, 3]),
+              Position.of([2, 2]),
+            ]
+          ]),
+          properties: {'name': 'poly2'},
+        );
+        final poly3 = Feature(
+          geometry: Polygon(coordinates: [
+            [
+              Position.of([4, 4, 10]), // With altitude
+              Position.of([5, 4, 10]),
+              Position.of([5, 5, 10]),
+              Position.of([4, 5, 10]),
+              Position.of([4, 4, 10]),
+            ]
+          ]),
+          properties: {'name': 'poly3'},
+        );
+
+        final collection = FeatureCollection(features: [poly1, poly2, poly3]);
+        final result = combine(collection);
+
+        expect(result.geometry, isA<MultiPolygon>());
+        expect((result.geometry as MultiPolygon).coordinates.length, 3);
+        // Check altitude preservation
+        expect((result.geometry as MultiPolygon).coordinates[2][0][0].length, 3);
+        expect((result.geometry as MultiPolygon).coordinates[2][0][0][2], 10);
+      });
+
+      test('preserves negative or high-altitude z-values', () {
+        // Test for extreme altitude values (negative and high)
+        final point1 = Feature(
+          geometry: Point(coordinates: Position.of([0, 0, -9999.5])), // Deep negative altitude
+          properties: {'name': 'deep_point'},
+        );
+        final point2 = Feature(
+          geometry: Point(coordinates: Position.of([1, 1, 9999.5])), // High positive altitude
+          properties: {'name': 'high_point'},
+        );
+
+        final collection = FeatureCollection(features: [point1, point2]);
+        final result = combine(collection);
+
+        expect(result.geometry, isA<MultiPoint>());
+        expect((result.geometry as MultiPoint).coordinates.length, 2);
+        
+        // Check extreme altitude preservation
+        expect((result.geometry as MultiPoint).coordinates[0].length, 3);
+        expect((result.geometry as MultiPoint).coordinates[0][2], -9999.5);
+        expect((result.geometry as MultiPoint).coordinates[1].length, 3);
+        expect((result.geometry as MultiPoint).coordinates[1][2], 9999.5);
+      });
+    });
+
+    // Error tests
+    group('validation and errors:', () {
+      test('throws error on mixed geometry types', () {
+        final point = Feature(
+          geometry: Point(coordinates: Position.of([0, 0])),
+          properties: {'name': 'point'},
+        );
+        final line = Feature(
+          geometry: LineString(coordinates: [
+            Position.of([0, 0]),
+            Position.of([1, 1]),
+          ]),
+          properties: {'name': 'line'},
+        );
+
+        final collection = FeatureCollection(features: [point, line]);
+        expect(() => combine(collection), throwsA(isA<ArgumentError>()));
+      });
+
+      test('throws error on empty collection', () {
+        final collection = FeatureCollection<Point>(features: []);
+        expect(() => combine(collection), throwsA(isA<ArgumentError>()));
+      });
+
+      test('throws error on unsupported geometry types (validation test)', () {
+        // This is a validation test - GeometryCollection is not claimed to be
+        // supported by combine(), which only works with Point, LineString, and Polygon.
+        final geomCollection = Feature(
+          geometry: GeometryCollection(geometries: [
+            Point(coordinates: Position.of([0, 0])),
+            LineString(coordinates: [
+              Position.of([0, 0]),
+              Position.of([1, 1]),
+            ]),
+          ]),
+          properties: {'name': 'geomCollection'},
+        );
+
+        final collection = FeatureCollection(features: [geomCollection, geomCollection]);
+        expect(() => combine(collection), throwsA(isA<ArgumentError>()));
+      });
+    });
+
+    // Property handling tests
+    group('property handling:', () {
+      test('has empty properties by default', () {
+        final point1 = Feature(
+          geometry: Point(coordinates: Position.of([0, 0])),
+          properties: {'name': 'point1', 'value': 42},
+        );
+        final point2 = Feature(
+          geometry: Point(coordinates: Position.of([1, 1])),
+          properties: {'name': 'point2', 'otherValue': 'test'},
+        );
+
+        final collection = FeatureCollection(features: [point1, point2]);
+        final result = combine(collection);
+
+        // By default, properties should be empty
+        expect(result.properties, isEmpty);
+      });
+
+      test('preserves properties from first feature when mergeProperties=true', () {
+        final point1 = Feature(
+          geometry: Point(coordinates: Position.of([0, 0])),
+          properties: {'name': 'point1', 'value': 42},
+        );
+        final point2 = Feature(
+          geometry: Point(coordinates: Position.of([1, 1])),
+          properties: {'name': 'point2', 'otherValue': 'test'},
+        );
+
+        final collection = FeatureCollection(features: [point1, point2]);
+        final result = combine(collection, mergeProperties: true);
+
+        // When mergeProperties is true, copies properties from first feature only
+        expect(result.properties!['name'], 'point1');
+        expect(result.properties!['value'], 42);
+        expect(result.properties!.containsKey('otherValue'), isFalse);
+      });
+    });
+
+    // GeoJSON otherMembers tests
+    group('GeoJSON compliance:', () {
+      test('preserves otherMembers in output', () {
+        // Create a source feature with otherMembers by parsing from JSON
+        final jsonStr = '''{
+          "type": "Feature",
+          "geometry": {
+            "type": "Point",
+            "coordinates": [0, 0]
+          },
+          "properties": {"name": "point1"},
+          "customField": "custom value",
+          "metaData": {"source": "test"}
+        }''';
+
+        final sourceFeature = Feature<Point>.fromJson(jsonDecode(jsonStr));
+        
+        // Create a feature collection with this feature
+        final collection = FeatureCollection(features: [sourceFeature]);
+        
+        // Combine (which should use the same feature as the source for the result)
+        final result = combine(collection, mergeProperties: true);
+        
+        // Convert to JSON and check for preservation of otherMembers
+        final resultJson = result.toJson();
+        
+        // Verify the otherMembers exist in the result
+        expect(resultJson.containsKey('customField'), isTrue);
+        expect(resultJson['customField'], 'custom value');
+        expect(resultJson.containsKey('metaData'), isTrue);
+        expect(resultJson['metaData']?['source'], 'test');
+      });
+    });
+  });
+}